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) -> ()
最初の2つの関数の型が同じ(String, Int) -> Stringであることに注目してください。シグネチャは異なり、2つの関数の働きも大きく違いますが、型は同じです。どちらもStringとIntを入力として受け取り、Stringを返します。そして、3つ目の関数の型からわかるように、その関数本体は何も返していないように見えても、実際には暗黙的に()を返しています。()は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
こんにちは、エージェント・ダブルヌル0111。
任務を引き受けるかどうかは任せますが、その内容は、UMBRA(本当に最低な手下たちの連合)の本部に潜入し、彼らの最新の、本当にひどい計画の書類を回収するための、2つのツールを書くことです。この任務を遂行するには、2つのツールが必要です。
秘密計画を手に入れたら、パスワードを知っている人だけがそれを取り戻せるように、計画を保護する必要があります。
これを行うには、関数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は、金庫の組み合わせをすべて、自分たちがいる部屋の番号に基づいて決めていることがわかりました。 金庫の組み合わせは、部屋の番号に特殊な関数を繰り返し適用して生成されます。 つまり、組み合わせの1つ目の数値は部屋の番号に関数を適用して得られ、2つ目は1つ目の数値に関数を適用して得られ、3つ目は2つ目の数値に関数を適用して得られます。
ただし、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)