ちょっとした小ネタです.クロージャ内でタプルな引数の各要素に変数名をバインドさせたいときの話です.
素直な書き方
クロージャの引数がタプルタイプだった場合,どのようにクロージャを記述していますか?
let f = { (t: (Int, Int)) -> Int in t.0 + t.1 }
と,このように記述するのが普通かと思います.
しかし,t.0
とかt.1
とかとタプルの要素番号を使うのではなく,個々の要素に名前をつけて使いたいと思うことがしばしばあります.となると,
let f = { (t: (Int, Int)) -> Int in
let (x, y) = (t.0, t.1)
return x + y
}
こんな感じで最初にタプルパターンで各要素に名前をつけるかと思います.
裏技的書き方,使い方
しかし,使う局面によっては以下のようなクロージャを使うことも可能です.
let f = { (x: Int, y: Int) -> Int in x + y }
なんてことはない,ただの2引数のクロージャですね.
さて,これをタプルを要素とするArrayのmapに放り込むと,
let a = [(1,2),(3,4),(5,6)]
let b = a.map(f)
println(b) // [3,7,11]
普通に実行できてしまいます.
クロージャのパラメータリストをタプルとして見なしてタプルパターンが使えてしまいます(そのように見える).
クロージャをダイレクトに与えれば,
a.map { (x, y) in x + y }
と(型推論によって)省略形が使えて見た目がスッキリする上,タプルの各要素への名前付け(変数束縛)が直接伝わってくる感じがして個人的にお気に入りです.
なぜ,こんなことができるのかは「Swiftの関数の引数は、常に一つ」に解説があります.
この仕掛けを知らないと,「なぜ2引数のクロージャをmapに渡してるんだ?」とパニックになります.
制約
なお,Swiftの文法上,パラメータリストはタプルとは別物なので入れ子構造な書き方は許されていません(当たり前).つまり,
let c = [(1,(2,3))]
c.map { (x, (y, z)) in x + y + z } // error
というネストしたパラメータリストは記述できないということです.こういう場合には,少々面倒ですが,
c.map { (x, y:(a: Int, b: Int)) in x + y.a + y.b }
のように,2要素目に要素名付きタプルをタイプとして書けば,一応全ての変数に名前をつけることができます.要素番号を使うよりは多少はマシという程度ですが.