トラック
/
Elixir
Elixir
/
演習
/
Gotta Snatch'Em All
Gotta Snatch'Em All

Gotta Snatch'Em All

学習演習

はじめに

MapSets

MapSetは、重複しない値の集まりで、Elixirにおける集合を表します。 あらゆる種類の値を入れることができ、順序という概念はありません。

なお、Setモジュールも存在しますが、MapSetが推奨されるようになり、非推奨となっています。

MapSet.new/0、MapSet.new/1、MapSet.new/2を使うと集合を作成でき、MapSet.to_list/1を使うとリストに変換できます。

MapSet.new()
# => MapSet.new([])

MapSet.new([2, 3, 3, 3, 1, 1, 2, "hello"])
# => MapSet.new([1, 2, 3, "hello"])

MapSet.new([2, 3, 3, 3, 1, 1, 2], fn n -> 10 * n end)
# => MapSet.new([10, 20, 30])

[2, 3, 3, 3, 1, 1, 2] |> MapSet.new() |> MapSet.to_list()
# => [1, 2, 3]

MapSetには順序という概念がないため、MapSet.to_list/1がソートされたリストを返すとは限りません。

要素の追加や削除は、MapSet.put/2とMapSet.delete/2で行えます。

集合の内容は、MapSet.size/1とMapSet.member?/2という関数で調べることができ、集合の比較は、MapSet.equal?/2、MapSet.subset?/2、MapSet.disjoint?/2で行えます。

a = MapSet.new([1, 10])
b = MapSet.new([1, 2, 3, 4, 5, 6, 7, 8, 9, 10])

MapSet.size(b)
# => 10

MapSet.member?(a, 10)
# => true

MapSet.subset?(a, b)
# => true

MapSet.disjoint?(a, b)
# => false

MapSet同士は、MapSet.union/2、MapSet.intersection/2、MapSet.difference/2、MapSet.symmetric_difference/2で組み合わせることができます。

a = MapSet.new([1, 10, 100])
b = MapSet.new([1, 2, 3, 4, 5, 6, 7, 8, 9, 10])

MapSet.union(a, b)
# => MapSet.new([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 100])

MapSet.intersection(a, b)
# => MapSet.new([1, 10])

MapSet.difference(a, b)
# => MapSet.new([100])

MapSet.difference(b, a)
# => MapSet.new([2, 3, 4, 5, 6, 7, 8, 9])

MapSet.symmetric_difference(b, a)
# => MapSet.new([2, 3, 4, 5, 6, 7, 8, 9, 100])

集合は、MapSet.filter/2、MapSet.reject/2、MapSet.split_with/2でフィルタリングしたり分割したりできます。

a = MapSet.new([1, 2, 3, 4, 5, 6, 7, 8, 9, 10])
even = fn n -> rem(n, 2) == 0 end

MapSet.filter(a, even)
# => MapSet.new([2, 4, 6, 8, 10])

MapSet.reject(a, even)
# => MapSet.new([1, 3, 5, 7, 9])

MapSet.split_with(a, even)
# => {MapSet.new([2, 4, 6, 8, 10]), MapSet.new([1, 3, 5, 7, 9])}

説明

Blorkemon™️のカードへの懐かしさは、まだまだ薄れる気配がありません。もう一度集め始めてしまっただけでなく、友達まで巻き込んでいます。

この演習では、カードのコレクションをMapSetで表します。というのも、存在するカードをすべて集めるのが目標なら、重複したカードは重要ではないからです。

1. コレクションを始める

友達にもBlorkemon™️熱にどっぷり浸かってほしいですよね。その一番の近道は、カードを1枚あげて、コレクションの第一歩を踏み出してもらうことです。

カードをコレクションに変換するGottaSnatchEmAll.new_collectionを実装しましょう。

GottaSnatchEmAll.new_collection("Newthree")
# => MapSet.new(["Newthree"])

2. コレクションを育てる

コレクションができたら、それは独り歩きを始め、育てていかなければなりません。

カードとコレクションを受け取り、2つの値を持つタプルを返すGottaSnatchEmAll.add_cardを実装しましょう。2つの値とは、そのカードがすでにコレクションに入っていたかどうかを示す真偽値と、そのカードを追加したコレクションです。

GottaSnatchEmAll.add_card("Scientuna", MapSet.new(["Newthree"]))
# => {false, MapSet.new(["Newthree", "Scientuna"])}

3. 交換を始める

友達がまたBlorkemon™️にハマった今なら、カードを交換して自分のコレクションを増やせます。

すべての交換が価値があるわけでも、そもそもできるわけでもありません。 持っていないカードは交換に出せませんし、すでに持っているカードのために手放すべきでもありません。

交換する2枚のカード(自分と相手のもの)と、現在のコレクションを受け取るGottaSnatchEmAll.trade_cardを実装しましょう。 戻り値は2つの値を持つタプルです。1つは、その交換が可能で価値があるかどうかを示す真偽値、もう1つは、実際に交換した場合に手元に残るコレクションです(実際には不可能な場合でも)。

GottaSnatchEmAll.trade_card("Scientuna", "Newthree", MapSet.new(["Scientuna"]))
# => {true, MapSet.new(["Newthree"])}

4. 同じカードは1枚だけ

フリーマーケットで古いカードの山を見つけます。 そのリストを自分のコレクションと比べるために、カードを並べ替えて重複を取り除く必要があります。

カードのリストを並べ替え、並べ替え済みで重複のないカードのリストを返すGottaSnatchEmAll.remove_duplicatesを実装しましょう。

GottaSnatchEmAll.remove_duplicates(["Newthree", "Newthree", "Newthree", "Scientuna"])
# => ["Newthree", "Scientuna"]

5. 相手が持っていないカード

自分のコレクションを誇りに思うときです。

自分のコレクションと他の誰かのコレクションを受け取り、その相手のコレクションが持っていないカードの枚数を返すGottaSnatchEmAll.extra_cardsを実装しましょう。

GottaSnatchEmAll.extra_cards(MapSet.new(["Scientuna"]), MapSet.new(["Newthree", "Scientuna"]))
# => 0

6. 全員が持っているカード

Blorkemon™️好きの友達と集まって、どのカードが一番ありふれているのかと考えます。

コレクションのリストを受け取り、すべてのコレクションが持っているカードを並べ替えたリストを返すGottaSnatchEmAll.boring_cardsを実装しましょう。

GottaSnatchEmAll.boring_cards([MapSet.new(["Scientuna"]), MapSet.new(["Newthree", "Scientuna"])])
# => ["Scientuna"]

7. すべてのカード

友達と力を合わせれば、Blorkemon™️のカードをすべて持っていることになるでしょうか?

コレクションのリストを受け取り、すべてのコレクションにある異なるカードの合計枚数を返すGottaSnatchEmAll.total_cardsを実装しましょう。

GottaSnatchEmAll.total_cards([MapSet.new(["Scientuna"]), MapSet.new(["Newthree", "Scientuna"])])
# => 2

8. キラキラで決まり

もうすぐ甥っ子が遊びに来ます。ここはひとつ、かっこいいところを見せたいところです。 子どもはキラキラしたものが好きですよね? Blorkemon™️のカードにもキラキラしたものがあります!

コレクションを受け取り、並べ替えたカードの2つのリストを持つタプルを返すGottaSnatchEmAll.split_shiny_cardsを実装しましょう。1つは"Shiny"で始まるすべてのカード、もう1つはそれ以外のカードです。

GottaSnatchEmAll.split_shiny_cards(MapSet.new(["Newthree", "Scientuna", "Shiny Scientuna"]))
# => {["Shiny Scientuna"], ["Newthree", "Scientuna"]}
GitHubで編集する リンクは新しいウィンドウまたはタブで開きます
Elixir Exercism

Gotta Snatch'Em Allを始める準備はできましたか?

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