関数

宣言

Swiftの関数の構文は次のようになります。


// String型とInt型の引数を受け取る関数
func showEncounterMessage(monster: String, count: Int) {
    print("\(count)匹の\(monster)が現れた!")
}

showEncounterMessage("スライム", 3) // 関数の呼び出し

funcの後に関数名を書き、()の間に引数名と型を必要な数だけ指定します。引数が不要な場合でも関数を呼び出す時には()は必要です。

戻り値を返す場合は、)の後に、-> をつけて型を指定します。


// String型とInt型の引数を受け取り、String型を返す関数
func getEncounterMessage(monster: String, count: Int) -> String {
    return "\(count)匹の\(monster)が現れた!"
}
let message = getEncounterMessage("スライム", 3)
getEncounterMessage("ゴブリン", 1)  // 戻り値を受け取らなくてもエラーにはならない

タプルを戻り値とすることで複数の値を返すことができます。


// 元号
enum Era {
    case Meiji, Taisho, Showa, Heisei
}
// 指定された元号の名前と開始年を返す
func getEraInfo(era: Era) -> (name: String, startYear: Int) {
    switch era {
    case .Meiji:
        return ("明治", 1868)
    case .Taisho:
        return ("大正", 1912)
    case .Showa:
        return ("昭和", 1926)
    case .Heisei:
        return ("平成", 1989)
    }
}

let showa = getEraInfo(.Showa)

次のようにオプショナルな戻り値を返すこともできます。


// 元号
enum Era {
    case Meiji, Taisho, Showa, Heisei, Unknown
}
// 指定された元号の名前と開始年を返す
func getEraInfo(era: Era) -> (name: String, startYear: Int)? {
    switch era {
    case .Meiji:
        return ("明治", 1868)
    case .Taisho:
        return ("大正", 1912)
    case .Showa:
        return ("昭和", 1926)
    case .Heisei:
        return ("平成", 1989)
    case .Unknown:
        return nil
    }
}

if let era = getEraInfo(.Showa) {
    print("元号:\(era.name) 開始年:\(era.startYear)")
}

外部引数名

関数の各引数には、引数名(ローカル名)の他に、分かりやすいラベル(外部名)をつけて、呼び出し時に使うことができます。ラベルをつける事で関数自体がより説明的になり機能や意味が伝わりやすくなります。


// RGB値を16進数文字列に変換
func rgbToString(red r:Int, green g:Int, blue b:Int) -> String {
    // 関数内では、各引数のローカル名を使用
    return String(format:"%02x%02x%02x", r, g, b)
}

let color = rgbToString(red: 255, green: 128, blue: 0)  // ff8000

引数の規定値

関数の宣言時に引数にデフォルト(既定)値を与えることができます。


func encloseText(text: String, prefix: String = "(", suffix: String = ")") -> String {
    return prefix + text + suffix
}

print(encloseText("りんご")) // (りんご)
print(encloseText("りんご", prefix:"【", suffix:"】")) // 【りんご】

既定値の与えられた引数を省略した場合、指定された既定値が引数として使用されます。

既定値をもつ引数は、特に外部名を指定しなければ、ローカル名が外部名となり、呼び出し側で既定値と異なる値を渡す時にラベルとして使用することができます。(引数名の前に#をつける必要はありません)

外部名を使いたくない場合は、次のように_(下線)を指定して無効にすることができます。(推奨はされないようです)


func encloseText(text: String, _ prefix: String = "(", _ suffix: String = ")") -> String {
    return prefix + text + suffix
}

print(encloseText("みかん", "[", "]"))   // [みかん]

引数の順番を次のようにすることも可能ですが、呼び出し方の一貫性を保つために、既定値付きの引数は引数リストの後方にもってくるように推奨されています。


// 次の様な引数の並びは推奨されない。
func encloseText(prefix: String = "(", suffix: String = ")", text: String) -> String {
    return prefix + text + suffix
}

print(encloseText("りんご")) // (りんご)
print(encloseText(prefix: "[", suffix:"]", "りんご")) // [りんご]

可変数個引数

Swiftでは同じ型の可変個の引数を渡すことができます。可変個の引数を受け取ることを示すには、型の後に...(ピリオドが3つ)を書きます。

関数の中では可変個引数は指定された型の配列として参照することができます。


// 文字列中に指定した文字のどれかが含まれればtrueを返す
func containsCharacter(text: String, characters: Character...) -> Bool {
    for ch in characters {
        for t in text {
            if ch == t {
                return true
            }
        }
    }
    return false
}

containsCharacter("abcdefg", "i") // false
containsCharacter("abcdefg", "g", "h", "i") // true

可変数個引数は引数リストの最後に配置する必要があります。もし既定値と可変数個引数をもつ関数が必要な場合は、既定値をもつ引数の後に可変数個引数を配置するようにします。


// 可変数個引数は引数の最後に
func someFunc(arg1: Int, arg2: String = "hogehoge", arg3: Int...) {
    :
}

定数引数と可変引数

関数の引数はデフォルトで定数として渡されます。(letが省略されています。引数名の前にletをつけても構いません)関数の中で引数の値を変更しようとするとエラーになります。


func printHelloMessage(text: String) {
    text = "Hello、" + text  // コンパイルエラー
    print(text);
}

引数名の前にvarをつけると、引数は可変変数となり、関数内での変更もできるようになります。但し、変更した引数の値を関数の外部で参照することはできません。


func printHelloMessage(var text: String) {
    text = "Hello、" + text
    print(text);
}

printHelloMessage("Swift")  // Hello, Swift

引数名の前にinoutをつけると、引数として渡した可変変数を関数の中で変更して、変更後の値を関数の外側でも参照できるようになります。

関数の呼び出し時に、変数の前に&をつけて呼び出します。また、ここで渡す変数は可変変数でなければなりません。定数を渡すとコンパイルエラーになります。
また、既定値をもつ引数や、可変数個引数にinout指定をすることはできません。varやletとinoutを同時に指定することもできません。


func incrementBy(inc: Int, inout val1: Int, inout val2: Int) {
    val1 += inc
    val2 += inc
}

var val1 = 100
var val2 = 200
incrementBy(10, &val1, &val2)
print("val1=\(val1), val2=\(val2)")   // val1=110, val2=210

関数型

関数も他の型と同様、型情報を持ちます。
例えば次の関数は、2つのInt型を引数にとり、Int型を返す関数で、型は、(Int, Int) -> Int となります。


func multiply(val: Int, #by: Int) -> Int {
    return val * by
}

次の関数の型は、(String, Int) -> () となります。


func showEncounterMessage(monster: String, count: Int) {
    print("\(count)匹の\(monster)が現れた!")
}

次の様に、関数型の変数を宣言し、関数名を代入して呼び出すことができます。


var f: (Int, Int) -> Int
f = multiply
var v = f(10, 3)    // 30 *ここでは外部名のbyは使えない

型推論が働くので、型を宣言しなくても代入することができます。但し、異なる型の関数を代入すると実行時エラーになります。


var f = multiply
var v = f(10, by:3)    // 30
f = showEncounterMessage    // 異なる型の代入は実行時エラー

関数自体を、他の関数への引数として使うことができます。


// 足し算
func add(a: Int, b: Int) -> Int {
    return a + b
}

// 引き算
func subtract(a: Int, b: Int) -> Int {
    return a - b
}

// 関数を渡して計算
func calculate(a: Int, b: Int, function:(Int, Int) -> Int) -> Int {
    return function(a, b)
}

calculate(10, 20, add)  // 30
let f = subtract
calculate(10, 20, f)    // -10

無名関数

関数の内容を次のように、名前を持たない関数として直接記述して渡すこともできます。


func calculate(a: Int, b: Int, function: (Int, Int) -> Int) -> Int {
    return function(a, b)
}

// 引数として乗算の結果を返す無名関数を記述
calculate(10, 20, {
    (val1: Int, val2: Int) in
    return val1 * val2
})

引数名と戻り値の型を書いて、in に続けて、関数の処理内容を記述します。

無名関数の引数に既定値を与えることはできません。

引数と戻り値の型は、関数の宣言から類推されるので次の様に型の指定を省くことができます。


calculate(10, 20, {
    val1, val2 in
    return val1 * val2
})

さらに、無名関数の引数は上の例のように名前をつけなくても、第一引数から順に$0, $1...として参照できます。また値を返すだけの関数の場合、return文自体も省略できるので、上の例は次のように簡略化して書くこともできます。


calculate(10, 20, { $0 * $1 })

さらにもう少し簡略化できます。Swiftでは、演算子も関数として実装されています。そして乗算に用いる * は、Int型同士を掛けてInt型を返す関数でもあるので、上の例ではそのまま渡すことができます。(*だけでなく、+、-、/、%等も同様です。)


calculate(10, 20, *)    // 200
calculate(10, 20, +)    // 30
calculate(10, 20, -)    // -10

上の無名関数の例はさらに別の書き方があります。引数で渡す関数が引数リストの最後の引数の場合、次のように、関数名()の後の、{ }の中に処理内容を記述することができます。


func calculate(a: Int, b: Int, function: (Int, Int) -> Int) -> Int {
    return function(a, b)
}

calculate(10, 20) {
    $0 * $1
}

引数が関数のみの場合は、関数名の後の()も不要です。


func sayHello(greeting:(String) -> String) -> () {
    print(greeting("Hello"))
}

sayHello { $0 + ", World" } // Hello, World
sayHello { "Hi, " + $0 }    // Hi, Hello

関数のネスト

次のように、関数の内部で別の関数を定義して使うことができます。


func addAndSubtract(val1: Int, val2: Int, val3:Int) -> Int {
    // 足し算
    func add(a: Int, b: Int) -> Int {
        return a + b
    }
    // 引き算
    func subtract(a: Int, b: Int) -> Int {
        return a - b
    }
    
    var result = add(val1, val2)
    result = subtract(result, val3)
    return result
}

addAndSubtract(10, 50, 20)  // 40

クロージャ

関数を呼び出す側のスコープで定義された変数を、内側の関数で参照したり変更することができます。これはクロージャと呼ばれるものです。


func calculate(val1: Int, val2: Int, val3:Int) -> Int {
    var x: Int?

    func calc1(a: Int, b: Int) -> Int {
        if x == nil {
            x = 100
        }
        return a + b
    }
    func calc2(a: Int, b: Int) -> Int {
        return a - b + (x == nil ? 0 : x!)
    }
    
    var result = calc1(val1, val2)
    result = calc2(result, val3)
    return result
}

calculate(10, 50, 20)   // 140

クロージャを使うと例えば次のように、呼び出される度に+1した値を返す関数オブジェクトを生成することができます。


func makeIncrementer(initValue: Int) -> () -> Int {
    var v = initValue
    func incrementer() -> Int {
        return ++v
    }
    
    return incrementer
}

let inc = makeIncrementer(10)
inc()   // 11
inc()   // 12
inc()   // 13

次の例では、1ではなく引数(第2引数)で指定した値を加算するようにしています。このように外側の関数の引数も内部の関数で参照することができます。


func makeIncrementer(initValue: Int, addValue: Int) -> () -> Int {
    var v = initValue
    func incrementer() -> Int {
        v += addValue
        return v
    }
    
    return incrementer
}

let inc = makeIncrementer(10, 5)
inc()   // 15
inc()   // 20
inc()   // 25

上の引数initValueにvarを指定し、さらにネストした関数を無名関数にするともう少し簡潔に記述できます。


func makeIncrementer(var initValue: Int, addValue: Int) -> () -> Int {
    return {
        initValue += addValue
        return initValue
    }
}

let inc = makeIncrementer(10, 5)
inc()   // 15
inc()   // 20
inc()   // 25