見出し画像

Swiftでプログラミング。 - Closures 1

クロージャは、自分のコードを再利用可能として機能的な自己完結型のブロックを作ります。 Swiftのクロージャは、CおよびObjective-Cのブロック、および他のプログラミング言語の"lambda"に似ています。

クロージャは、定数と変数が参照されている値を保持することができます。  定義されているコードを実行したときに保持することができます。これらは"closing over"として知られています。 Swiftは、保持した値のメモリ管理をします。

キャプチャの概念に慣れていなくても心配しないでください。これについては、以下の「値の取得」で詳しく説明しています。

関数で導入されるグローバル関数とネストされた関数は、実際にはクロージャの特殊なケースです。クロージャは、次の3つの形式のいずれかを取ります。

・クロージャである名前を持つグローバル関数は、値を保持しません。
・名前がある入れ子関数は、囲んでいる関数から値を保持した値を取得できるクロージャです。
・クロージャ式は、周囲の文脈からわかるように保持した値を簡単な構文で記述された名前のない関数です(無名関数)。

Swiftのクロージャー式は、簡潔でわかりやすい構文で書いたもので、すっきりとして明確にわかりやすくなっています。これらを最適化するために次のものが含まれます。

・コードの文脈からパラメータと戻り値のタイプを推測する
・単一式クロージャからの暗黙的なリターン
・短縮引数名
・末尾のクロージャ構文

Closure Expressions クロージャー式

入れ子関数は、より大きな関数の一部として名前を付た自己完結型のコードブロックとして便利に利用できます。 これをさらに完結に便利に使うために宣言せず、名前を付けずに、関数のような構造の短いバージョンを作成することがあります。 これは、関数を1つ以上の引数として取る関数またはメソッドを使用する場合に特に便利です。

クロージャ式は、簡潔で焦点を絞った構文で短く記述する方法です。 クロージャ式は、明確さや意図を失うことなく、短縮された形式でクロージャを記述するためのいくつかの構文が用意されています。 以下のクロージャ式の例は、sorted(by :)メソッドの1つの例を複数の反復にわたって改良することにより、これらの最適化しています。各反復は、同じ機能をより簡潔に表現できます。

The Sorted Method  ソートする方法

Swiftの標準ライブラリで、sorted(by :)と呼ばれるメソッドがあります。このメソッドは、指定したsorting closureで、既知の型の値の配列を並べ替えます。 並べ替えプロセスが完了すると、sorted(by :)メソッドは、古い配列と同じ型とサイズの新しい配列を返し、その要素は正しい並べ替え順序になります。 元の配列は、sorted(by :)メソッドによって変更されません。

以下のクロージャ式の例では、sorted(by :)メソッドを使用して、文字列値の配列をアルファベットの逆順で並べ替えています。 並べ替える初期配列は次のとおりです。

let names = ["Chris", "Alex", "Ewa", "Barry", "Daniella"]

sorted(by :)メソッドは、配列の内容と同じ型の2つの引数を取るクロージャを受け入れ、値が並べ替えられた後、最初の値が2番目の値の前に表示されるか後に表示されるかを示すBool値を返します。 sorting closureは、最初の値が2番目の値の前に表示される場合はtrueを返し、それ以外の場合はfalseを返す必要があります。

この例では、文字列値の配列を並べ替えているため、並べ替えクロージャはタイプ(String、String)-> Boolの関数である必要があります。

sorting closureを提供する1つの方法は、正しい型の通常の関数を記述し、それを引数としてsorted(by :)メソッドに渡すことです。

    func backward(_ s1: String, _ s2: String) -> Bool {
       return s1 > s2
   }
   var reversedNames = names.sorted(by: backward)
   // reversedNames is equal to ["Ewa", "Daniella", "Chris", "Barry", "Alex"]

最初の文字列(s1)が2番目の文字列(s2)より大きい場合、backward(_:_ :)関数はtrueを返し、ソートされた配列でs1がs2の前に表示される必要があることを示します。 文字列内の文字の場合、「より大きい」は「アルファベットの後半に現れる」を意味します。 これは、文字「B」が文字「A」より「大きい」ことを意味し、文字列「Tom」が文字列「Tim」よりも大きいことを意味します。 これにより、アルファベットの逆ソートが行われ、「Barry」が「Alex」の前に配置されます。

ただし、これは本質的に単一式の関数(a> b)を記述するためのかなり長い方法です。 この例では、クロージャ式の構文を使用して、sorting closureを短く記述することが望ましいでしょう。

Closure Expression Syntax クロージャ式の構文

クロージャ式の構文には、次の一般的な形式があります。

    { (parameters) -> return type in
       statements
   }

クロージャ式構文のパラメータは、in-outパラメータにすることができますが、デフォルト値にすることはできません。 可変個引数パラメーターに名前を付けると、可変個引数パラメーターを使用できます。 タプルは、パラメーター型および戻り型としても使用できます。

以下の例は、上からのbackward(_:_ :)関数のクロージャ式バージョンを示しています。

    reversedNames = names.sorted(by: { (s1: String, s2: String) -> Bool in
       return s1 > s2
   })

この inline closureのパラメータと戻り値の宣言は、backward(_:_ :)関数からの宣言と同じであることに注意してください。 どちらの場合も、(s1:String、s2:String)-> Boolと記述されます。 ただし、 inline closure式の場合、パラメータと戻り型は中括弧の外側ではなく、中括弧の内側に記述されます。

クロージャーの本体の開始は、inキーワードによって導入されます。 このキーワードは、クロージャのパラメータと戻り値のタイプの定義が終了し、クロージャの本体がまもなく開始されることを示します。

クロージャーの本体は非常に短いため、1行で書くこともできます。

reversedNames = names.sorted(by: { (s1: String, s2: String) -> Bool in return s1 > s2 } )

これは、sorted(by :)メソッドへの全体的な呼び出しが同じままであることを示しています。 括弧のペアは、メソッドの引数全体をラップします。 ただし、その引数は現在、 inline closureです。

Inferring Type From Context 文脈から型を推測

sorting closureは引数としてメソッドに渡されるため、Swiftはそのパラメーターの型と返す値の型を推測できます。 sorted(by :)メソッドは文字列の配列で呼び出されるため、その引数は(String、String)-> Bool型の関数である必要があります。 つまり、(String、String)型とBool型は、クロージャ式の定義の一部として記述する必要はありません。 すべての型を推測できるため、戻り矢印(->)とパラメーター名を囲む括弧も省略できます。

reversedNames = names.sorted(by: { s1, s2 in return s1 > s2 } )

inline closure式としてクロージャを関数またはメソッドに渡す場合、パラメータ型と戻り型を推測することが簡単であるため省略することが可能となります。

必要に応じて型を明示的にすることもできます。コードのあいまいさを回避する場合は、明示することが推奨されます。 sorted(by :)メソッドの場合、ソートが行われているという事実からクロージャーの目的は明らかであり、クロージャーは文字列値で行なっていることは想定可能で、この場合文字列の配列のソートを支援します。

Implicit Returns from Single-Expression Closures 単一表現のクロージャからの暗黙のリターン

前の例のこのバージョンのように、単一式のクロージャは、returnキーワードを省略することにより、単一の式の結果を暗黙的に返すことができます。

reversedNames = names.sorted(by: { s1, s2 in s1 > s2 } )

ここで、sorted(by :)メソッドの引数の関数型は、Bool値がクロージャによって返される必要があることを明確にしています。 クロージャの本体にはブール値を返す単一の式(s1> s2)が含まれているため、あいまいさはなく、returnキーワードは省略できます。

Shorthand Argument Names 省略引数名

Swiftは、inline closureでクロージャの引数の値を省略引数名、$ 0、$ 1、$ 2などの名前で参照します。

クロージャ式内でこれらの省略引数名を使用する場合は、クロージャの引数リストをその定義から省略できます。 短縮引数名のタイプは、予期される関数型から推測され、使用する最も大きい番号の短縮引数によって、クロージャが取る引数の数が決まります。 クロージャ式は完全に本体で構成されているため、inキーワードは省略できます。

reversedNames = names.sorted(by: { $0 > $1 } )

ここで、$ 0と$ 1は、クロージャーの最初と2番目の文字列引数を指します。 $ 1は最大数の省略引数であるため、クロージャーは2つの引数を取ると理解されます。 ここでのsorted(by :)関数は、引数が両方とも文字列であるクロージャを想定しているため、省略形の引数$ 0と$ 1は両方ともString型です。

Operator Methods 演算子メソッド

実際には、上記のクロージャ式を記述するさらに短い方法があります。 SwiftのString型は、大なり演算子(>)の文字列固有の実装を、String型の2つのパラメーターを持ち、Bool型の値を返すメソッドとして定義します。 これは、sorted(by :)メソッドに必要なメソッド型と完全に一致します。 したがって、大なり記号演算子を渡すだけで、Swiftは文字列固有の実装ができます。

reversedNames = names.sorted(by: >)

参考サイトです。


この記事が気に入ったらサポートをしてみませんか?