Swift さくっと確認したい基礎文法 [クロージャ(closure)]

  • 104
    いいね
  • 0
    コメント
この記事は最終更新日から1年以上が経過しています。

クロージャ(closure)

一言で言えば、名無しの関数式。

基本的な書き方

{(引数:引数の型) -> (戻り値の型) in
    // 処理
    return 戻り値
}

// a + b を足した結果を返すクロージャをaddFuncに代入
let addFunc = {(a:Int, b:Int) -> Int in
    return a + b
}

let result = addFunc(1,2)
println(result)  // 3

戻り値が一つの場合は、上のように()を省略できます。

クロージャを型推論でaddFuncに代入しましたが、
型をきちんと指定すると下記のようになります。

let addFunc: (Int,Int) -> Int = {(a:Int, b:Int) -> Int in
    return a + b
}

Intの引数が二つ、戻り値がInt一つの関数の型と同じです。

※ちなみに引数も戻り値もない場合は下記のように書きます。

var simpleClosure = {() -> () in
    var a = 1+1
    println(a) // 2
}

引数にクロージャをとる関数

map()関数

第一引数に指定した配列のすべての値に対して
クロージャで指定した処理を実行します。

map(配列, クロージャ)
let numbers = [0.02,0.4,0.26,0.8]

// 指定した配列内の数値一つずつをパーセンテージ化
let array = map(numbers, { (let num:Double) -> String in
    var percent = num*100
    return percent.description + "%"
})

// [2.0%, 40.0%, 26.0%, 80.0%]
println(array) 

下記のように、第二引数のクロージャを外に出す書き方もできます。

let array = map(numbers){(let num:Double) -> String in
    var percent = num*100
    return percent.description + "%"
}

型推論で型宣言を省略し、returnも省略すると
下記のようになります。

let array = map(numbers){num in (num*100).description + "%"}

1行で書くことができ、かなりスッキリとした印象です。

さらに...
クロージャの引数は$0,$1,$2 : 第1引数の値,第2引数の値,第3引数の値
というように指定することができます。

今回の場合は下記のように書くことができます。

// 第一引数を$0(上の式でいうnum)として処理
let array = map(numbers){($0*100).description + "%"}

ただあまり省略すると可読性が下がるので、
ほどほどにしたほうがよさそうです。

sorted()関数

Stringや配列の値を大きさによってソートする関数です。

sorted(配列, クロージャ)

Array<String>の場合(String()[])

下の例は、
String型の配列をabc順に並び替えています。
.uppercaseStringで大文字に変換して並び替えしています。

var cafeMenu: Array<String>
cafeMenu = ["Coffee","cappuccino","Latte","mocca"]

let sortedCafeMenu = sorted(cafeMenu, {(s1:String,s2:String) -> Bool in
    return (s1.uppercaseString < s2.uppercaseString)
})
println(sortedCafeMenu)

出力
"[cappuccino, Coffee, Latte, mocca]"

省略1

var cafeMenu: Array<String>
cafeMenu = ["Coffee","cappuccino","Latte","mocca"]

let sortedCafeMenu = sorted(cafeMenu, {(s1,s2) -> Bool in
    return (s1.uppercaseString < s2.uppercaseString)
})
println(sortedCafeMenu)

省略2

var cafeMenu: Array<String>
cafeMenu = ["Coffee","cappuccino","Latte","mocca"]

let sortedCafeMenu = cafeMenu.sorted {
    $0.uppercaseString < $1.uppercaseString
}
println(sortedCafeMenu)

Dictionary<String,Int>()の場合(Dictionary<String,Int>())

以下のようにしてDictionary型も並び替えができます。

String(メニューのabc順)で並び替え

var menuDictionary = Dictionary<String,Int>()
menuDictionary = ["Coffee":300,"cappuccino":400,"Latte":400,"mocca":450]

let sortedMenuDictionary = sorted(menuDictionary,{(d1:(String,Int),d2:(String,Int)) -> Bool in
    return d1.0.uppercaseString < d2.0.uppercaseString
})

println(sortedMenuDictionary)
出力
"[(cappuccino, 400), (Coffee, 300), (Latte, 400), (mocca, 450)]"

Int(値段順)で並び替え

var menuDictionary = Dictionary<String,Int>()
menuDictionary = ["Coffee":300,"cappuccino":400,"Latte":400,"mocca":450]

let sortedMenuDictionary = sorted(menuDictionary,{(d1:(String,Int),d2:(String,Int)) -> Bool in 
    return d1.0.uppercaseString < d2.0.uppercaseString
})

println(sortedMenuDictionary)
出力
"[(Coffee, 300), (Latte, 400), (cappuccino, 400), (mocca, 450)]"

クロージャを受け取る関数

クロージャは関数の引数としても利用できます。
下の例では、関数dicMap()の引数はDictionaryとクロージャです。

dicMap()関数内で、
Dictionaryの値をクロージャに渡して、結果を返すということをしています。

今回の例は、
ある商品リストの価格を、
クロージャで税込み価格にして返すという処理をしています。

// メニューのDictionary
var menuDictionary = ["Coffee":300.0,"cappuccino":400.0,"Latte":400.0,"mocca":450.0]

// 税込み価格を返すクロージャ
var getIntaxPrice = {(name:String, value:Double) -> (String,Double) in
    return (name,value*1.08)
}

// メニューのDictionaryを受け取って、税込み価格に変換したDictionaryを返す関数
func dicMap(var dic:Dictionary<String,Double>, closure:(String,Double) -> (String,Double)) -> Dictionary<String,Double> {

    for (key, value) in dic {
        println(value)
        let(theKey, newValue) = closure(key,value)
        dic[theKey] = newValue
    }
    return dic
}

// 税込み価格のメニュー
let inTaxMenuDictionary = dicMap(menuDictionary,getIntaxPrice)

println(inTaxMenuDictionary)
出力
[Coffee: 324.0, Latte: 432.0, mocca: 486.0, cappuccino: 432.0]

クロージャ間で値を保持する

関数内にクロージャを宣言する場合、
関数内のクロージャの外に宣言した変数の値を保持することができます。
staticなローカル変数と同じ扱いです。

func hp(max:Int) -> (Int) -> Int {

    // 保持される変数
    var nowHp = max

    func attacked(damage:Int) -> Int {
        if nowHp > damage{
            nowHp -= damage
        } else {
            nowHp = 0
        }
        return nowHp
    }
    return attacked
}
let hp100 = hp(100) // 100

var restHp = hp100(30)
println(restHp) // 70(100-30)
restHp = hp100(40)
println(restHp) // 30(70-40)
restHp = hp100(50)
println(restHp) // 0(30-50)

呼び出すごとに返ってくる値が小さくなっています。
関数内の値が保持されている証拠です。