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
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 関数内からしか呼び出すことができません。