トラック
/
Swift
Swift
/
演習
/
Double-Null0111:クロージャは永遠に
Double-Null0111:クロージャは永遠に

Double-Null0111:クロージャは永遠に

学習演習

はじめに

Swiftのクロージャは、自己完結したコードのまとまりで、引数を渡すとその計算が実行され、値を返します。クロージャはまた、自分の環境から値をキャプチャして、計算の中で使います。自己完結しているので、ほかの値と同じようにプログラムの中を受け渡ったり、定数や変数に代入したりできます。

クロージャはSwiftの関数とよく似ていると感じるかもしれません。実際そのとおりで、それにはちゃんとした理由があります。Swiftの関数はクロージャの特別な場合で、名前を持つことが必要で、少し違う構文で定義します。

Swiftの関数は技術的にはクロージャですが、Swiftで「クロージャ」と言うときはクロージャ式を指します。クロージャ式は、仮引数のリスト、戻り値の型、キーワードin、クロージャの本体をこの順に書き、全体を波括弧で囲みます。

{ (a: Double, b: Double) -> Double in
  return (a + b) / 2.0
}

これは型(Double, Double) -> Doubleのクロージャ式を定義しています。この型の関数と同じように、適切な型の引数を与えることで実行できます。

{ (a: Double, b: Double) -> Double in
  return (a + b) / 2.0
}(10.0, 15.0)
// returns 12.5

実行するたびに完全なクロージャ式を書くのはあまり便利ではないので、クロージャには、Swiftのほかの値と同じように名前を付けて代入できます。そうすれば、その名前を引数に適用して呼び出せます。

let mean = { (a: Double, b: Double) -> Double in
  (a + b) / 2.0
}

mean(13.0, 100.0)
// returns 56.5

上で見たように、クロージャは通常の関数と同じく、本体が単一の式であればキーワードreturnを省略できます。

クロージャは、高階関数と組み合わせて使われることが最も多く、引数として渡されたり、結果として返されたりします。

[11, 75, 3, 99, 53].contains(where: { (x: Int) -> Bool in x > 100 })
// returns false

["apple", "ball", "carrot"].sorted(by: { (s1: String, s2: String) -> Bool in s1.count < s2.count })
// returns ["ball", "apple", "carrot"]

func makeAdder(base: Int) -> (Int) -> Int {
  { (x: Int) -> Int in base + x }
}

説明

エージェントDouble-Null0111、また会いましたね。

UMBRAの勢力が再び動き出しました。その最新の計画を阻止するのはエージェントDouble-Null0111の任務です。情報部門によると、ミニオンたちは知事の舞踏会で悪臭爆弾を爆発させる計画を立てているとのことです。

ミニオンたちはとても不器用なので、研究室やオフィスでうっかり悪臭爆弾の起爆装置を作動させてしまうことがよくあります。そこで、悪臭爆弾の解除方法をいつも忘れてしまう彼らは、爆弾のID番号からワイヤーを切る順番を示す解除手順を生成するシステムを導入しました。

エージェントDouble-Null0111の仕事は、舞踏会の悪臭爆弾が爆発する前に解除できるようにするコードを書くことです。

1. 2本のワイヤーを入れ替えるクロージャを書く

悪臭爆弾にはそれぞれ、色の異なる3本のワイヤーがあります。 (String, String, String)のタプルを受け取り、最初の2つの要素の順番を入れ替えたタプルを返すクロージャを書きます。 このクロージャをflipという名前に代入します。

型はChangeClosure(またはsendableな型)でなければならない点に注意してください。型定義はスタブファイルに書かれています。 そうでないと、コードはコンパイルできません。

flip(("red", "yellow", "blue"))
// Returns ("yellow", "red", "blue")

2. ワイヤーを回転させるクロージャを書く

(String, String, String)のタプルを受け取り、ワイヤーの順番を左に回転させたタプルを返すクロージャを書きます。 このクロージャをrotateという名前に代入します。

型はChangeClosure(またはsendableな型)でなければならない点に注意してください。型定義はスタブファイルに書かれています。

rotate(("red", "yellow", "blue"))
// Returns ("yellow", "blue", "red")

3. ワイヤーのシャッフル生成器を実装する

悪臭爆弾のワイヤーを切る順番を割り出すには、悪臭爆弾のID番号に基づいて適切なシャッフル関数を生成する関数を実装する必要があります。

次の関数を実装します。

makeShuffle(
  flipper: @escaping ((String, String, String)) -> (String, String, String),
  rotator: @escaping ((String, String, String)) -> (String, String, String)
) -> ([UInt8], (String, String, String)) -> (String, String, String)

この関数は、2本のワイヤーを入れ替えるクロージャと3本のワイヤーを回転させるクロージャを入力として受け取り、クロージャを返します。 返されたクロージャは、悪臭爆弾のID番号と3本のワイヤーの順番を受け取り、ワイヤーを切るべき順番を計算します。 ID番号は8ビットでできており、各ビットがワイヤーを入れ替えるか回転させるかを決めます。

ID番号の各ビットについて、右端のビットから順に、ビットが0ならflipperクロージャを、1ならrotatorクロージャをワイヤーのタプルに適用し、新しいワイヤーの状態を得ます。 IDの8ビットすべてに対して適切なクロージャを適用したあと、ワイヤーの最終的な状態が、切るべき順番になります。

let shuffler = makeShuffle(flipper: flip, rotator: rotate)
// Returns ([UInt8], (String, String, String)) -> (String, String, String)
shuffler([1, 0, 0, 1, 1, 0, 1, 1], ("red", "yellow", "blue"))
// Returns ("red", "blue", "yellow")
GitHubで編集する リンクは新しいウィンドウまたはタブで開きます
Swift Exercism

Double-Null0111:クロージャは永遠にを始める準備はできましたか?

Exercismに登録すれば、35個のコンセプト116個の演習、そして本物の人間によるメンタリングとともに、Swiftを学んでマスターできます。すべて無料です。