함수는 타입이 있다. 섹시한 타입, 청순한 타입…모든 건 타입이 있다. 그러한 타입으로 구별도 하고 같은 부류로 묶기도 한다.
func addTwoInts(a: Int, b: Int) -> Int {
return a + b
}
func multiplyTwoInts(a: Int, b: Int) -> Int {
return a * b
}
위에 정의한 두개의 함수를 보자 하나는 두개의 정수를 더해서 리턴하는 함수이고 다른 하나는 곱한값을 리턴하는 함수이다.
우리는 두개의 함수를 (Int, Int) -> Int 이런 타입을 이해한다.
즉, 두개의 정수를 받아서 하나의 정수를 리턴하는 타입이다.
func printHelloWorld() {
println("hello, world")
}
여기 또 다른 함수가 있다. 입력 파라미터가 없고 리턴도 없다. 그냥 프린트 달랑 하나하고 끝난다.
결국 이 녀석은 () -> () 이렇게 이해한다. 즉 입력없고 리턴도 없다.
스위프트에서 함수는 데이타 타입이다. 즉 정수, 실수, 문자형처럼 그냥 데이타 타입이다.
그러니 타입을 나타내는 String, Int 등이 올 자리에 함수도 타입처럼 올수 있다.
var welcomeMessage: String = "Hello"
var someInt: Int = 3
var mathFunction: (Int, Int) -> Int = addTwoInts
즉 위에 있는 코드의 예처럼 String, Int 타입처럼 (Int, Int) -> Int 이 올 수 있다. 즉
mathFunction 이라는 변수의 이름은 정수 두개를 받아서 결과값으로 정수형을 리턴하는 어떤 함수라도 설정할 수 있다는것이다. 여기에서는 그래서 두개의 정수를 더하는 함수를 mathFunction 으로 설정했다.
물론 mathFunction 에 multiplyTwoInts를 설정할 수도 있다.
결국 mathFunction은 두개의 정수를 받아서 정수를 리턴하는 어떠한 함수와도 잘 맞는다. 매우 융통성이 있고 어떤 할수라도 처리를 할 수 있으니 개발할 때 정해지지 않는 어떤 작업을 실행할때 적절하게 선택되게 할 수 있다.
객체 지향 프로그래밍 개념에서 다형성이라는 개념인데 매우 유용하다.
프로젝트의 소스를 아래 코드로 수정해보자
func addTwoInts(a: Int, b: Int) -> Int {
return a + b
}
func multiplyTwoInts(a: Int, b: Int) -> Int {
return a * b
}
override func viewDidLoad() {
super.viewDidLoad()
var mathFunction: (Int, Int) -> Int = addTwoInts
println(“addTwoInts: \(mathFunction(2, 3))”)
mathFunction = multiplyTwoInts
println(“multiplyTwoInts: \(mathFunction(2, 3))”)
}
결국 같은 mathFunction 을 호출하지만 다른 함수를 대입하여 결과값을 얻을 수 있다.
함수를 데이타 타입으로 이해했음으로 함수를 다른 함수의 파라미터로 전달 할 수도 있다.
어떤 함수가 매우 유연하려면 함수를 호출할때 그 기능을 설정하면 매우 유연해보인다.
두개의 정수를 받아서 어떤 처리를 하고 그 결과를 리턴하는 함수를 파라미터로 받아 결과를 출력하는 함수를 정의해보자. 헐~~ 뭔가 길고 복잡해 보인다. 일단 코드를 보자
func addTwoInts(a: Int, b: Int) -> Int {
return a + b
}
func multiplyTwoInts(a: Int, b: Int) -> Int {
return a * b
}
func printMathResult(mathFunction: (Int, Int) -> Int, a: Int, b: Int) {
println("Result: \(mathFunction(a, b))")
}
override func viewDidLoad() {
super.viewDidLoad()
var mathFunction: (Int, Int) -> Int = addTwoInts
printMathResult(mathFunction, a: 3, b: 5)
mathFunction = multiplyTwoInts
printMathResult(mathFunction, a: 3, b: 5)
}
printMathResult 라는 함수는 (Int, Int) -> Int 타입을 파라미터로 받는다. 즉 두개의 정수를 받아 정수를 리턴하는 함수 타입이다. 그리고 두개의 정수를 받아서 프린트 하는 간단한 함수가 정의된다.
출력되는 결과를 보면 같은 함수를 호출하지만 설정된 함수가 다른 함수이므로 다른 결과를 나타낸다.
실질적인 게임등 프로그래밍에 있어 매우 유용한 함수가 만들어 질 수 있다.
이제 함수의 리턴값으로 함수가 리턴되는 경우를 보자.
func stepForward(input: Int) -> Int {
return input + 1
}
func stepBackward(input: Int) -> Int {
return input - 1
}
func chooseStepFunction(backwards: Bool) -> (Int) -> Int {
return backwards ? stepBackward : stepForward
}
override func viewDidLoad() {
super.viewDidLoad()
var currentValue = 3
let moveNearerToZero = chooseStepFunction(currentValue > 0)
println("Counting to zero:")
while currentValue != 0 {
println("\(currentValue)... ")
currentValue = moveNearerToZero(currentValue)
}
println("zero!")
}
조금 길지만 stepForward 라는 함수는 어떤 정수값을 받으면 하나 더해서 리턴하는 함수다. stepBackward 는 반대로 하나 적은 값을 돌려준다. chooseStepFunction 은 참이나 거짓을 받아서 참이면 stepBackward 를 거짓이면 stepForward를 돌려준다. 즉 (Int) -> Int 타입의 함수를 돌려준다.
위에서 정의한 stepForward 와 stepBackward는 둘다 (Int) -> Int 타입이다 따라서 두 함수중에 하나를 리턴하면 된다.
결과적으로 0보다 큰값이 오면 참이되고 stepBackward가 선택된다. moveNearerToZero 함수는 stepBackward 함수가 되고 현재 값이 하나 줄어들어서 설정된 후 다시 while문을 반복한다.
현재 값이 0이되면 루프를 빠져 끝난다.