Nested Functions のネストされた関数は、大きな関数の一部として、独立したコードブロックに名前を付けて定義する便利な手段です。しかし、完全な宣言を名前とせず、関数のような構成をより短く記述できるほうが効果的な場合もあります。特に、引数として関数を 1 つ以上受け取る関数やメソッドを扱うときがそうです。
クロージャ式は、簡潔かつ明瞭なシンタックスで、クロージャをインラインに記述する手段です。クロージャ式には、明確さや意図を損なうことなく、短縮形式でクロージャを記述するために最適化されたシンタックスがいくつかあります。以降の例では、sort(_:)
メソッドを洗練させていく最適化を例に、同じ機能をより簡潔な方法で表現する例を示しています。
sort メソッド
Swift の標準ライブラリに、型がわかっている値の配列を、ソート用クロージャからの出力をもとにしてソートする sort(_:)
メソッドがあります。ソート処理を完了後、sort(_:)
メソッドは元の配列と同じ型、同じサイズの、要素が並べ替えられた新たな配列を返します。元の配列は sort(_:)
メソッドによって変更されません。
クロージャ式の次の例は、String
値の配列をアルファベットの逆順にソートするために sort(_:)
メソッドを使用します。ソートする前の初期状態の配列です。
let names = ["Chris", "Alex", "Ewa", "Barry", "Daniella"]
配列の中身と同じ型の引数を 2 つ受け取り、ソート後に 1 つ目の値が 2 つ目の値の前後どちらになるかを Bool
値で返すクロージャを受け取ります。ソート用クロージャは、1 つ目の値が 2 つ目の値より前になる場合には true
を返し、そうでない場合には false
を返す必要があります。
この例では String
値の配列をソートしているため、ソート用クロージャは (String, String) -> Bool
型の関数である必要があります。
ソート用クロージャを提供する方法の一つは、適切な型の普通の関数を記述し、それを引数として sort(_:)
メソッドに渡すことです。
func backwards(s1: String, _ s2: String) -> Bool {
return s1 > s2
}
var reversed = names.sort(backwards)
// reversed は ["Ewa", "Daniella", "Chris", "Barry", "Alex"]
1 つ目の文字列 (s1
) が 2 つ目の文字列 (s2
) より大きい場合、関数 backwards(_:_:)
はソート後の配列で s1
が s2
より前になることを示す true
を返します。文字列の文字にとっての「大きい」とは、「アルファベット順で後になる」ことを意味します。つまり、文字 "B"
は文字 "A"
より大きく、文字列 "Tom"
は文字列 "Tim"
より大きいです。このコード例はアルファベットの逆順にソートするため、"Barry"
は "Alex"
の前に置かれています。
しかし、式が 1 つの関数 (a > b
) を記述するためには、この方法はいくぶん冗長です。この例では、クロージャ式シンタックスを使用してソート用クロージャをインラインに記述するほうが望ましいです。
クロージャ式シンタックス
クロージャ式シンタックスは、次のような一般形式になります。
{ (parameters) -> return type in
statements
}
クロージャ式シンタックスには、定数パラメータ、変数パラメータ、inout
パラメータを使用することができます。デフォルト値を持たせることはできません。可変長パラメータに名前を付けた場合には、可変長パラメータを使用することができます。パラメータの型および戻り値の型として、タプルを使用することもできます。
次の例は、関数 backwards(_:_:)
のクロージャ式バージョンです。
reversed = names.sort({ (s1: String, s2: String) -> Bool in
return s1 > s2
})
このインラインクロージャのパラメータと戻り値の型の宣言は、関数 backwards(_:_:)
の宣言と同じであることに注目してください。両ケースともに、(s1: String, s2: String) -> Bool
として記述されています。しかし、インラインクロージャ式では、パラメータと戻り値の型は波括弧の外側でなく、内側に記述されています。
クロージャの本体は in
キーワードで始まります。このキーワードは、クロージャのパラメータと戻り値の型の定義が終了し、クロージャの本体が始まることを示しています。
クロージャの本体が短いため、1 行に記述することさえできます。
reversed = names.sort( { (s1: String, s2: String) -> Bool in return s1 > s2 } )
sort(_:)
メソッドの呼び出し全体は同じで、丸括弧がメソッドの引数全体を囲んでいます。しかし、引数はインラインクロージャになっています。
文脈から型を推論
ソート用クロージャは引数としてメソッドに渡されるため、Swift はパラメータの型と返す値の型を推論することができます。sort(_:)
メソッドは文字列の配列で呼び出されているため、その引数を (String, String) -> Bool
型の関数にする必要があります。そのため、(String, String)
と Bool
型をクロージャ式の定義の一部として記述する必要はありません。すべての型を推論することができるため、リターンアロー (->
) とパラメータ名を囲む丸括弧も省略することができます。
reversed = names.sort( { s1, s2 in return s1 > s2 } )
インラインクロージャ式として関数やメソッドにクロージャを渡すときには、パラメータの型と戻り値の型を推論することが可能です。その結果、関数やメソッドの引数としてクロージャを使用するときに、インラインクロージャを完全な形式で記述する必要はありません。
それでも型を明示することは可能で、コードの読み手にとってのあいまいさを避けたい場合には、型を明示することが推奨されます。sort(_:)
メソッドのケースでは、ソートしている事実からクロージャの目的が明確で、文字列の配列をソートしていることから、クロージャが String
値を扱っているということを読み手が安全に想定できます。
式が 1 つのクロージャからの非明示的なリターン
式が 1 つのクロージャは、前の例で見た宣言から return
キーワードを省略して、非明示的に式の結果を返すことができます。
reversed = names.sort( { s1, s2 in s1 > s2 } )
この sort(_:)
メソッドの引数の関数型では、クロージャによって Bool
値が返されるということが明らかです。クロージャの本体が Bool
値を返す式 (s1 > s2
) 1 つであるため、あいまいでなく、return
キーワードを省略することができます。
簡略引数名
Swift は自動的に、$0
、$1
、$2
などの名前でクロージャの引数の値を参照できる簡略引数名をインラインクロージャに与えます。
クロージャ式で簡略引数名を使用する場合、定義からクロージャの引数リストを省略することができ、簡略引数名の数値と型は関数型から推論されます。クロージャ式が本体だけで構成されるため、in
キーワードも省略できます。
reversed = names.sort( { $0 > $1 } )
この例では、クロージャの 1 つ目と 2 つ目の String
引数を、$0
と $1
は参照しています。
演算子関数
実際には、クロージャ式をさらに短く記述する方法があります。Swift の String
型は、String
型の引数を 2 つ受け取って Bool
値の値を返す関数として、「より大きい演算子 (>
)」の文字列固有の実装を定義しています。これは、sort(_:)
メソッドで必要な関数型と一致しています。従って、単にその「より大きい演算子」を渡すことができ、その文字列固有の実装を使用するということを Swift が推論します。
reversed = names.sort(>)
演算子関数についての詳細は、Operator Functions を確認してください。
Portions of this page are translations based on work created and shared by Apple and used according to terms described in the Creative Commons Attribution 4.0 International License.