LoginSignup
159
162

More than 5 years have passed since last update.

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

Last updated at Posted at 2015-05-13

クロージャ(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)

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

159
162
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
159
162