クロージャ(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)
呼び出すごとに返ってくる値が小さくなっています。
関数内の値が保持されている証拠です。