學習軌道
/
Elixir
Elixir
/
練習
/
Gotta Snatch'Em All
Gotta Snatch'Em All

Gotta Snatch'Em All

學習練習

簡介

MapSet

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™️ 的狂熱,而最好的方式就是送他們一張卡片,幫他們的收藏起步。

實作 GottaSnatchEmAll.new_collection,它會把一張卡片轉換成一個收藏。

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

2. 擴充收藏

一旦有了收藏,它就會有自己的生命,必須不斷成長。

實作 GottaSnatchEmAll.add_card,它接受一張卡片和一個收藏,並回傳一個含有兩個值的元組:一個布林值,表示這張卡片是否已經在收藏中,以及加入這張卡片後的收藏。

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

3. 開始交換

既然朋友們又為 Blorkemon™️ 瘋狂了,你就可以藉由交換卡片來壯大自己的收藏。

不是每次交換都值得做,甚至不一定做得成。 你不能交換自己沒有的卡片,也不該拿卡片去換自己已經有的。

實作 GottaSnatchEmAll.trade_card,它接受兩張要交換的卡片(你的和對方的)以及你目前的收藏。 回傳值是一個含有兩個值的元組:一個布林值,表示這次交換是否可行又值得做,以及完成交換後你會得到的收藏(即使實際上根本不可能交換)。

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

4. 每種卡片只能有一張

你在跳蚤市場找到一批舊卡片。 你必須把卡片排序並移除重複的,才能把清單和你的收藏做比較。

實作 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™️ 卡片也會有閃亮版本喔!

實作 GottaSnatchEmAll.split_shiny_cards,它接受一個收藏,並回傳一個含有兩份排序卡片清單的元組:一份是所有以 "Shiny" 開頭的卡片,另一份是其他卡片。

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,全部免費。