함수 대부분은 작업의 가장 작은 단위이자 하나의 작은 프로그램이기도 합니다.
swift에서 함수는 일급 객체
이기 때문에 하나의 값으로도 사용할 수 있습니다.
다음 조건을 모두 충족해야 일급 객체
가 될 수 있습니다.
스위프트의 함수는 이 조건을 모두 충족할 수 있기에 함수를 일급 객체
로 취급합니다.
함수가 일급 객체
가 된다는 의미는 다양한 종류의 함수를 호출하고, 전달하고, 반환하는 등의 동작만으로 프로그램을 구현할 수 있다는 뜻입니다.
함수와 메서드는 기본적으로 같습니다.
구조체, 클래스 열거형 등 특정 타입에 연관되어 사용하는 함수를 메서드, 모듈 전체에서 전역적으로 사용될 수 있는 함수를 그냥 함수라고 부릅니다.
조건문이나 반복문 같은 스위프트의 다른 문법과 달리 함수에서는 소괄호 ()
를 생략할 수 없습니다.
스위프트의 함수는 재정의(오버라이드)와 중복정의(오버로드)를 모두 지원합니다.
따라서 매개변수의 타입이 다르면 같은 이름의 함수를 여러개 만들 수 있고 매개 변수의 개수가 달라도 같은 이름의 함수를 만들 수 있습니다.
스위프트의 함수는 자유도가 굉장히 높은 문법 중 하나 입니다.기본으로 함수의 이름과 매개 변수, 반환 타입 등을 사용하여 함수를 정의합니다.
func
입니다.()
로 감싸줍니다.->
를 사용하여 어떤 타입이 반환될 것인지 명시해줍니다.return
입니다.func introduce(name: Stirng) -> String {
// return "제 이름은" + name + "입니다"
"제 이름은" + name + "입니다"
}
함수 내부의 코드가 단 한줄의 표현이고, 그 표현의 결괏값의 타입이 함수의 반환 타입과 일치한다면 return
키워드를 생략할 수 있습니다.
func helloWorld() -> String {
return "Hello, World!"
}
print(helloWorld()) // Hello, World!
func sayHello(myName: String, yourName: String) -> String {
return "Hello \(yourName)! I'm \(myName)"
}
print(sayHello(myName: "yagom", yourName: "toma"))
// Hello yagom! I'm toma
위에서 sayHello(myName:yourName:)
함수를 호출 할 때 myName과 yourName이라는 매개변수 이름을 사용했습니다.
매개변수 이름과 더불어 전달인자 레이블을 지정해줄 수 있습니다.
보통 함수를 정의할 때 매개변수를 정의하면 매개변수 이름과 전달인자 레이블을 같은 이름으로 사용할 수 있지만 레이블을 별도로 지정하면 함수 외부에서 매개변수의 역할을 좀 더 명확히 할 수 있습니다.
전달인자 레이블을 사용하려면 함수 정의에서 매개변수 이름 앞에 한칸을 띄운 후 전달인자 레이블을 지정합니다.
스위프트에서 기본적으로 사용하는 키워드 대부분은 매개변수 이름으로 사용할 수 없습니다. 하지만 이름을 지정해줄 때 백쿼트(`)로 이름을 감싸주면 대부분의 키워드를 이름으로 사용할 수 있습니다
ex. `var`
func sayHello(from myName: String, to name: String) -> String {
return "Hello \(name)! I'm \(myName)"
}
print(sayHello(from: "toma", to: "yagom"))
// Hello yagom! I'm toma
함수 내부에서 전달인자 레이블을 사용할 수 없으며 함수를 호출할 때는 매개변수 이름을 사용할 수 없습니다.
전달인자 레이블을 사용하고 싶지 않다면 와일드 카드 식별자 _
를 사용하면 됩니다.
전달인자 레이블을 변경하면 함수의 이름 자체가 변경됩니다.
→ 전달인자 레이블만 다르게 써주더라도 함수 중복 정의(오버로드)로 동작할 수 있습니다.
func sayHello(to name: String, _ times: Int) -> String {
var result: String = ""
for _ in 0..<times {
result += "Hello \(name)!" + " "
}
return result
}
fun sayHello(to name: String, repeatCount times: Int) -> String {
var result: String = ""
for _ in 0..<times {
result += "Hello \(name)!" + " "
}
return result
}
print(sayHello(to: "Chope", 2))
print(sayHello(to: "Chope", repeatCount: 2))
스위프트 함수에서는 매개변수마다 기본값을 지정할 수 있습니다.
매개변수가 전달되지 않으면 기본값을 사용합니다.
매개변수 기본값이 있는 함수는 함수를 중복 정의한 것처럼 사용할 수 있습니다.
기본값이 없는 매개변수는 중요한 값을 전달할 가능성이 높기 때문에
기본값이 있는 매개변수 앞에 배치하는 것이 좋습니다.
func sayHelloToFriends(me: Stirng, friends names: String...) -> String {
var result: String = ""
for friend in names {
result += "Hello \(friend)! "
}
result += "I'm " + me + "!"
return result
}
함수의 전달인자로 값을 전달할 때는 보통 값을 복사해서 전달합니다.
값이 아닌 참조를 전달하려면 입출력 매개변수를 사용합니다.
값 타입 데이터의 참조를 전달인자로 보내면 함수 내부에 참조하여 원래 값을 변경합니다.
이 방법은 외부의 값에 어떤 영향을 줄지 모르기 때문에 함수형 프로그래밍 패러다임에서는 지양하는 패턴입니다.
var numbers: [Int] = [1, 2, 3]
func nonReferenceParameter(_ arr: [Int]) {
var copiedArr: [Int] = arr
copiedArr = 1
}
func referenceParameter(_ arr: inout [Int]) {
arr[1] = 1
}
nonReferenceParameter(numbers)
print(numbers[1]) // 2
referenceParameter(&numbers)
print(numbers[1]) // 1
값이 반환이 굳이 필요하지 않은 경우, 반환 값이 없는 함수를 만들 수 있습니다.
반환값이 없는 함수라면 반환 타입 "없음"을 의미하는 Void
로 표기하거나 생략합니다.
func sayHelloWorld() {
print("Hello, World!")
}
스위프트의 함수는 일급 객체이므로 하나의 데이터 타입으로 사용할 수 있습니다. 즉 각 함수는 매개변수 타입과 반환타입으로 구성된 하나의 타입으로 사용할 수 있습니다.
(매개변수 타입의 나열) -> 반환 타입
함수를 하나의 데이터 타입으로 나타내는 방법입니다.
func sayHello(name: String, times: Int) -> String {
}
sayHello 함수의 타입은 (String, Int) -> String
입니다.
func sayHelloToFriends(me: String, names: String...) -> String {
}
sayHelloToFriends 함수의 타입은 (String, String...) -> String
입니다.
func sayHello() {
}
sayHello 함수의 타입은 아래와 같습니다. (다 같은 표현)
(Void) -> Void
() -> Void
() -> ()
typealias CalculateTowInts = (Int, Int) -> Int
func addTwoInts(_ a: Int, _ b: Int) -> Int {
return a + b
}
func multiplyTwoInts(_ a: Int, _ b: Int) -> Int {
return a * b
}
var mathFunction: CalculateTwoInts = addTwoInts
mathFunction(2, 5) // 7
mathFunction = multiplyTwoInts
mathFunction(2, 5) // 10
// 전달인자로 함수를 전달받는 함수
func printMathResult(_ mathFunction: CalculateTowInts, _ a: Int, _ b: Int) {
print("Result: \(mathFunction(a, b))")
}
// 특정 조건에 따라 적절한 함수를 반환해주는 함수
func chooseMathFunction(_ toAdd: Bool) -> CalculateTwoInts {
return toAdd ? addTwoInts : multiplyTwoInts
}
스위프트는 데이터 타입의 중첩이 자유롭습니다. 예를 들어 열거형 안에 또 하나의 열거형, 클래스 안에 또다른 클래스가 들어올 수 있습니다.
함수의 중첩은 함수 안에 함수를 넣을 수 있다는 의미입니다.
-3 | -2 | -1 | 0 | 1 | 2 | 3 |
---|
원점이 0 이고 좌로는 음수, 우로는 양수로 이루어진 보드입니다.
특정 위치에서 원점으로 이동하는 함수를 만드려고 합니다.
typealias MoveFunc = (Int) -> Int
func goRight(_ currentPosition: Int) -> Int {
return currentPosition + 1
}
func goLeft(_ currentPosition: Int) -> Int {
return currentPosition - 1
}
func functionForMove(_ shouldGoLeft: Bool) -> MoveFunc {
return shouldGoLeft ? goLeft : goRight
}
var postion: Int = 3
let moveToZero: MoveFunc = functionForMove(position > 0)
while position != 0 {
position = moveToZero(position)
}
goLeft와 goRight가 사소한 기능의 차이일 뿐 원점을 찾아가는 목적은 같습니다. 따라서 굳이 모듈 전역에서 사용할 필요가 없습니다.
그래서 사용 범위를 함수를 하나의 함수 안쪽으로 배치하여 중첨 함수로 구현하고 필요할 때만 외부에서 사용할 수 있도록 구현해봅시다.
typealias MoveFunc = (Int) -> Int
func functionForMove(_ shouldGoLeft: Bool) -> MoveFunc {
func goRight(_ currentPosition: Int) -> Int {
return currentPosition + 1
}
func goLeft(_ currentPosition: Int) -> Int {
return currentPosition - 1
}
return shouldLeft ? goLeft : goRight
}
var position: int = -4
let moveToZero: MoveFunc = functionForMove(position > 0)
while position != 0 {
position = moveToZero(position)
}
전역함수가 많은 큰 프로젝트에서는 전역으로 사용이 불필요한 goRight(_:)
함수와 goLeft(:_)
함수의 사용 범위를 중첩함수를 통해 더 명확하고 깔끔하게 표현할 수 있습니다.
스위프트에는 종료되지 않는 함수가 있습니다.
종료되지 않는다는 의미는 정상적으로 끝나지 않은 함수라는 뜻입니다.
비반환 함수(메서드)
라고 합니다.
func crashAndBurn() -> Never {
fatalError("Something very, very bad happened")
}
crashAndBurn() // 프로세스 종료 후 오류 보고
func someFunction(isAllIsWell: Bool) {
guard isAllIsWell else {
print("마을에 도둑이 들었습니다!")
crashAndBurn()
}
print("All is well")
}
someFunction(isAllIsWell: true) // All is well
someFunction(isAllIsWell: false) // 마을에 도둑이 들었습니다!
// 프로세스 종료 후 오류 보고
프로그래머가 의도적으로 반환 값을 사용하지 않을 경우 컴파일러가 함수의 결과값을 사용하지 않았다는 경고를 보낼 때도 있습니다.
이런 경우 함수의 반환 값을 무시해도 된다는 @discardableResult
선언 속성을 사용하면 됩니다.
func say(_ something: String) -> String {
return something
}
@discardableResult
func discarableResultSay(_ something: String) -> String {
return something
}
say("Hello") // 반환 값을 사용하지 않아 컴파일러가 경고를 표시할 수 있음
discarableResultSay("Hello") // 반환 값을 사용하지 않아도 컴파일러가 경고하지 않음