Swift 함수 시그니처에서 이름과 레이블을 모두 지우면, 함수의 타입만 남아요.
func shoppingList(item: String, quantity: Int) -> String {
"You need to buy \(quantity) \(item)."
}
// shoppingList: (String, Int) -> String
func repeater(_ phrase: String, times: Int) -> String {
var result = ""
for _ in 1...times {
result += phrase
}
return result
}
// repeater: (String, Int) -> String
func printString(line: String) {
print(line)
}
// printString: (String) -> ()
처음 두 함수의 타입은 (String, Int) -> String으로 같은데, 두 시그니처는 서로 다르고 두 함수의 동작 방식도 아주 다르다는 점에 주목해요. 둘 다 String과 Int를 입력으로 받아 String을 반환해요. 그리고 세 번째 함수의 타입에서 볼 수 있듯이, 함수 본문이 아무것도 반환하지 않는 것처럼 보여도 실제로는 Void 타입의 유일한 값인 ()를 암묵적으로 반환해요.
이런 함수 타입은 Swift의 다른 타입과 똑같은 방식으로 쓸 수 있어요. 예를 들어 변수나 상수를 선언할 때 사용할 수 있죠. 그리고 Swift에서 함수는 값이기 때문에, 이미 있는 함수를 이런 상수나 변수에 대입할 수 있어요.
var stringAndIntToString: (String, Int) -> String
stringAndIntToString = shoppingList
stringAndIntToString("carrots", 3)
// => "You need to buy 3 carrots."
다른 변수나 상수와 마찬가지로, 대입하는 값은 올바른 타입이어야 해요.
stringAndIntToString = printShoppingList
// Error: Cannot assign value of type '(String) -> ()' to type '(String, Int) -> String'
함수 타입은 다른 타입을 쓸 수 있는 곳이면 어디든 쓸 수 있으므로, 매개변수 타입으로도 쓸 수 있고 다른 함수에 전달해 그 함수 안에서 호출할 수도 있어요.
func apply3(to str: String, function f: (String, Int) -> String) -> String {
f(str,3)
}
// apply3: ([String], (String, Int) -> String) -> [String]
apply3(to: "eggs", function: shoppingList)
// => "You need to buy 3 eggs."
apply3(to: "eggs", function: repeater)
// => "eggseggseggs"
마찬가지로 함수 타입은 함수의 반환 타입으로도 쓸 수 있어요. 다시 말해 다른 함수를 만들어 반환하는 함수를 작성할 수 있죠. 이런 함수를 만들 때, 반환되는 함수는 부모 함수에 전달된 다른 매개변수나 부모 함수 안에서 만들어진 지역 변수를 사용할 수 있어요.
func makeAdder(base: Int) -> (Int) -> Int {
func adder(_ i: Int) -> Int {
base + i
}
return adder
}
// makeAdder: (Int) -> (Int) -> Int
let add10 = makeAdder(base: 10)
// add10: (Int) -> Int
let subtract20 = makeAdder(base: -20)
// subtract10: (Int) -> Int
add10(5)
// => 15
subtract20(5)
// => -15
안녕하세요, Double-Null0111 요원.
이 임무를 수락하기로 했다면, UMBRA(United Minions Being Really Awful) 본부에 잠입해서 그들의 최신 정말 끔찍한 계획에 대한 설계도를 빼내는 데 필요한 두 가지 도구를 만들게 될 거예요.
비밀 계획을 손에 넣었다면, 비밀번호를 아는 사람만이 그 계획을 다시 꺼낼 수 있도록 보호해야 해요.
이를 위해 protectSecret(_ secret: String, withPassword password: String) -> (String) -> String 함수를 구현해야 해요.
이 함수는 비밀번호로 추정되는 문자열을 입력으로 받는 또 다른 함수를 반환해요.
입력한 비밀번호가 맞으면 새 함수는 숨겨진 비밀을 반환하지만, 비밀번호가 틀리면 "Sorry. No hidden secrets here."를 반환해요.
let secretFunction = protectSecret("Steal the world's supply of french fries!", withPassword: "5up3r53cr37")
secretFunction("Open sesame")
// Returns "Sorry. No hidden secrets here."
secretFunction("5up3r53cr37")
// Returns "Steal the world's supply of french fries!"
UMBRA는 모든 금고 조합을 금고가 위치한 방의 번호를 바탕으로 만든다는 사실을 알아냈어요. 금고 조합은 방 번호에 특별한 함수를 반복해서 적용해 생성해요. 즉, 조합의 첫 번째 숫자는 방 번호에 함수를 적용한 결과이고, 두 번째 숫자는 첫 번째 숫자에 함수를 적용한 결과이며, 세 번째 숫자는 두 번째 숫자에 함수를 적용한 결과예요.
그런데 UMBRA는 조합을 생성하는 데 쓰는 함수를 수시로 바꾸기 때문에, 도구가 조합을 생성하려면 방 번호와 그에 맞는 함수를 둘 다 받아야 해요.
필요한 조합을 생성하려면 generateCombination(forRoom room: Int, usingFunction f: (Int) -> Int) -> (Int, Int, Int) 함수를 구현해요.
func makeCombo(_ x: Int) -> Int {
(x * 13) % 256
}
generateCombination(forRoom: 227, usingFunction: makeCombo)
// Returns (135, 219, 31)
Exercism에 가입하고 Swift 트랙을 개념 35개연습 문제 116개, 그리고 실제 사람의 멘토링과 함께 배우고 익혀 보세요. 모두 무료예요.