iBooks にある “The Swift Programming Language” の勉強メモ。Objective-C と C を普段書いている自分から、ちょっと馴染みがないものを特にまとめておきます。目次は こちら。
今回は、Function に関して。長かったので 前編 はこちら。
Function
6. 可変引数(variadic)の関数の定義の仕方
C の printf のように、引数の数が呼び出す際に決まるタイプの関数の定義の仕方。
1 2 3 4 5 6 7 8 9 10 11 | func arithmeticMean(numbers: Double...) -> Double { var total: Double = 0 for number in numbers { total += number } return total / Double(numbers.count) } arithmeticMean(1, 2, 3, 4, 5) // returns 3.0, which is the arithmetic mean of these five numbers arithmeticMean(3, 8, 19) // returns 10.0, which is the arithmetic mean of these three numbers |
引数の型の名前の後ろに … と打つことで、この引数が可変であるということを定義できます。この可変引数は、関数内で、コンスタント配列として使用することができます。この可変引数を定義する際に、引数リストの一番最後に置きます。それにより、可変引数以外に引数がある場合に、曖昧さを防ぎます。もし、デフォルト値を持つ引数がある場合にも、この可変引数を一番最後に置きます。
7. 変数引数とコンスタンス引数
関数の引数はデフォルトでコンスタンスです。そんため、関数内でこのコンスタンスを変更することができません。もし受け取った引数を変更しながら処理したい場合には、関数定義時にその引数に var と指定します。これにより、関数内で変数を別に定義する必要がなくなります。
1 2 3 4 5 6 7 8 9 10 11 | func alignRight(var string: String, count: Int, pad: Character) -> String { let amountToPad = count - countElements(string) for _ in 1...amountToPad { string = pad + string } return string } let originalString = "hello" let paddedString = alignRight(originalString, 10, "-") // paddedString is equal to "-----hello" // originalString is still equal to "hello |
string が 変数引数です。関数内でこの引数の値を変更したとしても、関数を呼び出した側でその変更が反映されるわけではありません。
8. inout
関数の処理が終わった後も、その関数の変更を保持したい場合、C の場合にはポインタがありますが、Swift でも inout というキーワードを使用するようです。
1 2 3 4 5 6 7 8 9 10 11 | func swapTwoInts(inout a: Int, inout b: Int) { let temporaryA = a a = b b = temporaryA } var someInt = 3 var anotherInt = 107 swapTwoInts(&someInt, &anotherInt) println("someInt is now \(someInt), and anotherInt is now \(anotherInt)") // prints "someInt is now 107, and anotherInt is now 3 |
よくある swap 関数です。二つの変数の値を入れ替えます。関数定義時に引数の前に inout をつけ、関数呼び出し時に & をつけるだけです。注意点としては、引数として渡せるのは、変数だけです。コンスタンスやリテラルは渡すことができません。また、inout キーワードがついた引数には、デフォルト値を指定することもできません。また、可変引数(…)も使用できません。
9. Function Type
関数も型のように使用することができます。例えば、次のコード。
1 2 3 4 5 6 | func addTwoInts(a: Int, b: Int) -> Int { return a + b } func multiplyTwoInts(a: Int, b: Int) -> Int { return a * b } |
この 2 つの関数は、2つの Int 引数を取り、1 つの Int を返す Function Type を持つと言うことができます。他の、Int や Double といった型と同じようにこの Function Type を使用することができます。
1 | var mathFunction: (Int, Int) -> Int = addTwoInts |
そして、この代入された関数を Function Type ((Int, Int) -> Int) を持つ mathFunction 変数を使用して呼び出すことができます。
1 2 | println("Result: \(mathFunction(2, 3))") // prints "Result: 5 |
C の関数へのポインタみたいですね。
1 |
|
もちろん Swift の型推論に頼ることもできます。
1 | let anotherMathFunction = addTwoInts |
この Function Type を関数の引数として使用できます。
1 2 3 4 5 | func printMathResult(mathFunction: (Int, Int) -> Int, a: Int, b: Int) { println("Result: \(mathFunction(a, b))") } printMathResult(addTwoInts, 3, 5) // prints "Result: 8 |
printMathResult 関数は、引数として Function Type と 2 つの Int を受け取ります。
また、返り値として、Function Type を使用することもできます。
1 2 3 4 5 6 7 8 9 10 | 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 } |
この chooseStepFunction 関数は、引数に Bool を取り、返り値として、Function Type((Int) -> Int)を返します。
1 2 3 | var currentValue = 3 let moveNearerToZero = chooseStepFunction(currentValue > 0) // moveNearerToZero now refers to the stepBackward() function |
moveNearerToZero というコンスタンスが、chooseStepFunction の返り値を受け取っています。
10. 関数のネスト
今までの関数は全てグローバルでした。関数を他の関数内に定義してローカルな関数を定義することができます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | func chooseStepFunction(backwards: Bool) -> (Int) -> Int { func stepForward(input: Int) -> Int { return input + 1 } func stepBackward(input: Int) -> Int { return input - 1 } return backwards ? stepBackward : stepForward } var currentValue = -4 let moveNearerToZero = chooseStepFunction(currentValue > 0) // moveNearerToZero now refers to the nested stepForward() function while currentValue != 0 { println("\(currentValue)... ") currentValue = moveNearerToZero(currentValue) } stepForward(1) // err |
この chooseStepFunction 関数はこの文脈ではグローバル関数となり、どこからでも呼び出すことができます。しかし、このグローバル関数が内にネストされている stepForward と stepBackward という関数は、chooseStepFunction 関数内からしか呼び出すことができません。