Menu

Category

Archive

logo


The Swift Programming Language ~ Functions

2014-06-09 17:30:00 +0900
  • このエントリーをはてなブックマークに追加

iBooks にある "The Swift Programming Language" の勉強メモ。Objective-C と C を普段書いている自分から、ちょっと馴染みがないものを特にまとめておきます。目次は こちら

今回は、Function に関して。長かったので 前編 はこちら。

Function

6. 可変引数(variadic)の関数の定義の仕方

C の printf のように、引数の数が呼び出す際に決まるタイプの関数の定義の仕方。

 1 func arithmeticMean(numbers: Double...) -> Double {
 2     var total: Double = 0
 3     for number in numbers {
 4         total += number
 5     }
 6     return total / Double(numbers.count)
 7 }
 8 arithmeticMean(1, 2, 3, 4, 5)
 9 // returns 3.0, which is the arithmetic mean of these five numbers
10 arithmeticMean(3, 8, 19)
11 // returns 10.0, which is the arithmetic mean of these three numbers

引数の型の名前の後ろに ... と打つことで、この引数が可変であるということを定義できます。この可変引数は、関数内で、コンスタント配列として使用することができます。この可変引数を定義する際に、引数リストの一番最後に置きます。それにより、可変引数以外に引数がある場合に、曖昧さを防ぎます。もし、デフォルト値を持つ引数がある場合にも、この可変引数を一番最後に置きます。

7. 変数引数とコンスタンス引数

関数の引数はデフォルトでコンスタンスです。そんため、関数内でこのコンスタンスを変更することができません。もし受け取った引数を変更しながら処理したい場合には、関数定義時にその引数に var と指定します。これにより、関数内で変数を別に定義する必要がなくなります。

 1 func alignRight(var string: String, count: Int, pad: Character) -> String {
 2     let amountToPad = count - countElements(string)
 3     for _ in 1...amountToPad {
 4         string = pad + string
 5     }
 6     return string
 7 }
 8 let originalString = "hello"
 9 let paddedString = alignRight(originalString, 10, "-")
10 // paddedString is equal to "-----hello"
11 // originalString is still equal to "hello

string が 変数引数です。関数内でこの引数の値を変更したとしても、関数を呼び出した側でその変更が反映されるわけではありません。

8. inout

関数の処理が終わった後も、その関数の変更を保持したい場合、C の場合にはポインタがありますが、Swift でも inout というキーワードを使用するようです。

 1 func swapTwoInts(inout a: Int, inout b: Int) {
 2     let temporaryA = a
 3     a = b
 4     b = temporaryA
 5 }
 6 
 7 var someInt = 3
 8 var anotherInt = 107
 9 swapTwoInts(&someInt, &anotherInt)
10 println("someInt is now \(someInt), and anotherInt is now \(anotherInt)")
11 // prints "someInt is now 107, and anotherInt is now 3

よくある swap 関数です。二つの変数の値を入れ替えます。関数定義時に引数の前に inout をつけ、関数呼び出し時に & をつけるだけです。注意点としては、引数として渡せるのは、変数だけです。コンスタンスやリテラルは渡すことができません。また、inout キーワードがついた引数には、デフォルト値を指定することもできません。また、可変引数(...)も使用できません。

9. Function Type

関数も型のように使用することができます。例えば、次のコード。

1 func addTwoInts(a: Int, b: Int) -> Int {
2     return a + b
3 }
4 func multiplyTwoInts(a: Int, b: Int) -> Int {
5     return a * b
6 }

この 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 println("Result: \(mathFunction(2, 3))")
2 // prints "Result: 5

C の関数へのポインタみたいですね。

もちろん Swift の型推論に頼ることもできます。

1 let anotherMathFunction = addTwoInts

この Function Type を関数の引数として使用できます。

1 func printMathResult(mathFunction: (Int, Int) -> Int, a: Int, b: Int) {
2     println("Result: \(mathFunction(a, b))")
3 }
4 printMathResult(addTwoInts, 3, 5)
5 // prints "Result: 8

printMathResult 関数は、引数として Function Type と 2 つの Int を受け取ります。

また、返り値として、Function Type を使用することもできます。

 1 func stepForward(input: Int) -> Int {
 2     return input + 1
 3 }
 4 func stepBackward(input: Int) -> Int {
 5     return input - 1
 6 }
 7 
 8 func chooseStepFunction(backwards: Bool) -> (Int) -> Int {
 9     return backwards ? stepBackward : stepForward
10 }

この chooseStepFunction 関数は、引数に Bool を取り、返り値として、Function Type((Int) -> Int)を返します。

1 var currentValue = 3
2 let moveNearerToZero = chooseStepFunction(currentValue > 0)
3 // moveNearerToZero now refers to the stepBackward() function

moveNearerToZero というコンスタンスが、chooseStepFunction の返り値を受け取っています。

10. 関数のネスト

今までの関数は全てグローバルでした。関数を他の関数内に定義してローカルな関数を定義することができます。

 1 func chooseStepFunction(backwards: Bool) -> (Int) -> Int {
 2     func stepForward(input: Int) -> Int { return input + 1 }
 3     func stepBackward(input: Int) -> Int { return input - 1 }
 4 
 5     return backwards ? stepBackward : stepForward
 6 }
 7 var currentValue = -4
 8 let moveNearerToZero = chooseStepFunction(currentValue > 0)
 9 // moveNearerToZero now refers to the nested stepForward() function
10 while currentValue != 0 {
11     println("\(currentValue)... ")
12     currentValue = moveNearerToZero(currentValue)
13 }
14 
15 stepForward(1) // err

この chooseStepFunction 関数はこの文脈ではグローバル関数となり、どこからでも呼び出すことができます。しかし、このグローバル関数が内にネストされている stepForward と stepBackward という関数は、chooseStepFunction 関数内からしか呼び出すことができません。