トラック
/
Swift
Swift
/
演習
/
熟練のカクテル調合師
熟練のカクテル調合師

熟練のカクテル調合師

学習演習

はじめに

switch文

switch文を使うと、1つの値を複数の候補と比較できます。 複数のelse-if文を使うよりも、簡潔にコードを書けます。 switch文はswitchキーワードから始まり、その後に比較する値を書きます。 次に、caseキーワードの後に比較対象の値を書いて、各ケースを定義します。 どのケースにも一致しないときのデフォルトのケースは、defaultキーワードで定義します。 else-if文と同じように、switch文は最初に一致したケースに対応するコードブロックだけを実行します。

switch value {
case 1:
    print("One")
case 2:
    print("Two")
default:
    print("Other")
}

次の例を見てみましょう。

if str == "apple" {
    print("Let's bake an apple crumble")
} else if str == "lemon" {
    print("Let's bake a lemon meringue pie!")
} else if str == "peach" {
    print("Let's bake a peach pie!")
} else {
    print("Let's buy ice cream.")
}

これは、次のように書き換えられます。

switch str {
case "apple":
    print("Let's bake an apple crumble")
case "lemon":
    print("Let's bake a lemon meringue pie!")
case "peach":
    print("Let's bake a peach pie!")
default:
    print("Let's buy ice cream.")
}

whileループ

whileループは、ある条件が真である間、コードを実行し続ける制御フローの文です。 Swiftには、whileループとrepeat-whileループの2種類があります。 whileループでは、ループ本体を実行する前に条件が評価されます。条件が偽なら、ループ本体は一度も実行されません。 Swiftのwhileループは、次のような構造になっています。

while <#condition#> {
  <#code#>
}

このループはcountが0より大きい間実行され、その結果、2、1、"Liftoff!"と出力します。

var count = 2
while count > 0 {
  print("\(count)…")
  count -= 1
}
print("Liftoff!")

// prints:
// 2…
// 1…
// Liftoff!

repeat-whileループはwhileループに似ていますが、条件はループ本体を実行した後に評価されます。 つまり、条件が偽であっても、ループ本体は必ず少なくとも1回は実行されます。 Swiftのrepeat-whileループは、次のような構造になっています。

repeat {
  <#code#>
} while <#condition#>

この例では条件が偽なので、ループは1回だけ実行されます。 repeat-whileループなので、条件が評価される前にループ本体が1回実行されます。

var count = 2
repeat {
  print("\(count)…")
} while count < 0
print("Liftoff!")

制御転送

Swiftには、ループの通常の制御フローを変えるための文がいくつかあります。 これらは制御転送文と呼ばれます。 returnはすでに見てきました。このコンセプトでは、continue、break、ラベルなど、その他のものを紹介します。 fallthroughはこのコンセプトでは扱いませんが、Swiftの書籍で読むことができます。

continue

ループの現在の繰り返しに残っている文を最後まで実行せず、早めに次の繰り返しへ進みたいことがあります。 こういうときは、continueキーワードを使います。 continueが実行されると、ループは次の繰り返しを実行できるかどうかの判定に移ります。つまり、whileループとrepeat-whileループではwhileの判定、for-inループではシーケンスに次の要素があるかどうかの判定です。

count = 1
while count < 6 {
  count += 1
  if count == 4 { continue }
  print(count)
}

// prints:
// 2
// 3
// 5

break

breakは、ループから早めに抜けるために使います。 breakが実行されると、ループはすぐに終了し、プログラムはループの次にある最初の文から続きます。

for fruit in ["banana", "grapes", "apple", "strawberry", "kiwi", "lemon"] {
  if !fruit.count.isMultiple(of: 2) { break }
  print(fruit)
}

// prints:
// banana
// grapes

ラベル

ループが入れ子になっているとき、breakやcontinueを使っているループを含む外側のループを抜けたり再開したりするために、breakやcontinueを使いたい場合があります。 そのような場合は、ラベルを使って、抜けたり再開したりするループを指定できます。 ループを始めるwhile、repeat、forの前に、名前とコロンを書くと、そのループにラベルを付けられます。

このようにラベルを使うとどんな効果があるのか、次のループで見てみましょう。

outerLoop: for fruit in ["banana", "grapes", "apple", "strawberry", "kiwi", "lemon"] {
  print("\n--- \(fruit) ---")
  for letter in fruit {
    guard letter != "n" else { break outerLoop }
    print(letter)
  }
}

// prints:
// --- banana ---
// b
// a

説明

あなたは腕利きのミクソロジストとして、夜のにぎわいを楽しむ人たちのためにおいしい飲み物を作っています。でも、仕事を手伝ってくれるコードを少し書けば、もっと楽にできると思ったのです。

1. 注文にかかる時間を求める

飲み物の種類によって、準備にかかる時間は異なります。

  • "beer"、"soda"、"water"はそれぞれ0.5分です。
  • "shot"は1.0分、"mixed drink"は1.5分です。
  • "fancy drink"は2.5分です。
  • "frozen drink"は3.0分です。

関数timeToPrepare(drinks:)を実装してください。この関数は、用意する必要がある飲み物を表す文字列の配列を保持する引数drinksを取ります。関数は、配列内のすべての飲み物を用意するのにかかる合計時間を返すようにしてください。

timeToPrepare(drinks: ["beer", "frozen drink", "shot"])
// returns 4.5

2. ライムウェッジを補充する

シフトを始めるときにまずやるべきことの1つは、これからのシフトに備えて、ライムウェッジの小さな容器がいっぱいになっているか確認することです。ライムの大きさによって取れるウェッジの数は変わり、"small"のライムからは6個、"medium"のライムからは8個、"large"のライムからは10個のウェッジが取れます。

まだウェッジが足りなくて、切るライムもまだ残っている間は、配列から最初のライムを取り出し、必要な数からそのライムで取れるウェッジの数を引きます。必要な数に達するか、ライムがなくなるまでこれを続けます。

関数makeWedges(needed:limes:)を実装してください。この関数は、切る必要があるウェッジの数を保持する引数neededと、切るために用意されているライムの大きさを表す文字列の配列を保持する引数limesを取ります。関数は、切ったライムの数を返すようにしてください。

makeWedges(needed: 25, limes: ["small", "small", "large", "medium", "small"])
// returns 4

3. シフトを終える

おめでとうございます! 長い一日の仕事ももうすぐ終わり、まもなく家に帰って新しいアプリの開発に取りかかれます。でも、注文を次のシフトに引き継いで帰る前に、まだ続いている飲み物の注文をさばかなければなりません。

シフトに残り時間がある限り、配列の最初の注文を作り、その注文を配列から取り除きます。次に、その注文の準備にかかった時間を、関数timeToPrepare(drinks:)に従ってシフトの残り時間から引きます。

関数finishShift(minutesLeft:remainingOrders:)を実装してください。この関数は、シフトの残り時間(0より大きいことが保証されています)と、空でない飲み物の注文の配列を受け取ります。関数は、シフトが終わるまでに手をつけられなかった注文の配列を返すようにしてください。

finishShift(minutesLeft: 5, remainingOrders: [["beer", "frozen drink", "shot"], ["fancy drink", "soda"], ["beer", "beer", "water"], ["mixed drink", "frozen drink"]])
// returns [["beer", "beer", "water"], ["mixed drink", "frozen drink"]]

4. 特定の注文を記録する

ビールとソーダを新鮮なまま保つには、それらの注文と、注文された時刻を記録する必要があります。

関数orderTracker(orders: [(drink: String, time: String)]) -> (beer: (first: String, last: String, total: Int)?, soda: (first: String, last: String, total: Int)?)を実装してください。この関数は、注文と時刻のリストを受け取ると、ビールとソーダの最初と最後の注文時刻、それぞれの注文の合計数を含むオプショナルのタプルのペアを返し、それらの飲み物の注文が1つもなかった場合はnilを返します。他の飲み物を記録する必要はありません。

関数orderTracker(orders:)を実装してください。この関数はタプルの配列を受け取ります。各タプルは、注文された飲み物と、その注文が入った時刻を保持しています。関数は、ビールが最初に注文された時刻と最後に注文された時刻、ビールの注文の合計数、ソーダが最初に注文された時刻と最後に注文された時刻、ソーダの注文の合計数を保持するタプルを返すようにしてください。返す形式は次のタプルです:(beer: (first: String, last: String, total: Int)?, soda: (first: String, last: String, total: Int)?)

let orders = [
    (drink: "beer", time: "10:01"), (drink: "frozen drink", time: "10:02"),
    (drink: "shot", time: "10:05"), (drink: "fancy drink", time: "10:06"),
    (drink: "soda", time: "10:09"), (drink: "beer", time: "10:15"), (drink: "beer", time: "10:22"),
    (drink: "water", time: "10:26"), (drink: "mixed drink", time: "10:28"),
    (drink: "frozen drink", time: "10:33"),
]

orderTracker(orders: orders)

// returns (beer: (first "10:01", last "10:22", total 3), soda: (first "10:09", last "10:09", total 1))
GitHubで編集する リンクは新しいウィンドウまたはタブで開きます
Swift Exercism

熟練のカクテル調合師を始める準備はできましたか?

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