得分王

得分王

學習練習

簡介

Dict

Elm 中的Dict是不可變的字典,可以包含零或多組鍵值對。

字典是透過 Dict模組中定義的函式和運算子來操作的。

字典的型別標註寫法如下

Dict String (List Int) --> a dict with String keys and List Int values
Dict Int String --> a dict with Int keys and String values

可以這樣建立字典:

empty : Dict Int String
empty = Dict.empty

singleValue = Dict.singleton 5 "Value for key 5" --> Dict Int String

twoValues = Dict.fromList [ ( "Alice", 0 ), ( "Bob", 1 ) ] --> Dict String Int

可以使用get取得項目。 由於鍵可能不存在於字典中,也可能存在,結果會包在Maybe裡。

alice = Dict.fromList [ ( 0, "Alice" ) ]
keyExists = Dict.get 0 alice --> Just "Alice"
keyNotPresent = Dict.get 1 alice --> Nothing

可以使用insert新增項目。 如果鍵已經在字典中,對應的項目會被取代。

alice = Dict.fromList [ ( "Alice", 0 ) ]
aliceAndBob = Dict.insert "Bob" 1 alice  --> Dict.fromList [ ( "Alice", 0 ), ( "Bob", 1 ) ]
aliceAndAlice = Dict.insert "Alice" 1 alice  --> Dict.fromList [ ( "Alice", 1 ) ]

可以使用update更新項目。 如果鍵不存在,更新函式會以Nothing被呼叫。

alice = Dict.fromList [ ( 0, "Alice" ) ]
aliceUpperCase = Dict.update 0 (Maybe.map String.toUpper) alice --> Dict.fromList [ ( 0, "ALICE" ) ]
aliceNoCase = Dict.update 0 (\_ -> Nothing) alice --> Dict.empty
bobUpperCase = Dict.update 1 (\_ -> Just "BOB") alice --> Dict.fromList [ ( 0, "Alice" ), ( 1, "BOB" ) ]
bobNoCase = Dict.update 1 (\_ -> Nothing) alice --> Dict.fromList [ ( 0, "Alice" ) ]

可以使用remove移除項目。 如果鍵不存在,就不會有任何改變。

alice = Dict.fromList [ ( 0, "Alice" ) ]
stillAlice = Dict.remove 1 --> ( 0, "Alice" )
empty = Dict.remove 0 alice --> Dict.empty

可以使用fromList把鍵值對陣列轉換成字典。

alice = Dict.fromList [ ( 0, "Bob" ), ( 0, "Alice" ) ]
--> True Dict.fromList [ ( 0, "Alice" ) ]

可以使用toList把項目轉換成陣列。 陣列會依鍵排序。

aliceAndBob = Dict.fromList [ ( 1, "Alice" ), ( 0, "Bob" ) ]
bobAndAlice = Dict.toList aliceAndBob
--> [ ( 0, "Bob" ), ( 1, "Alice" ) ]

可以使用filter移除多個項目。

aliceAndBob = Dict.fromList [ ( "Alice", 1 ), ( "Bob", 0 ) ]
bob = Dict.filter (\name count -> name == "Bob") aliceAndBob --> Dict.fromList [ ( "Bob", 0 ) ]

可以使用map轉換項目。

alice = Dict.fromList [ ( "Alice", 0 ) ]
empty = Dict.map (\player count -> count + 1) alice --> Dict.fromList [ ( "Alice", 1 ) ]

可以使用merge合併或轉換字典。

aliceAndBob = Dict.fromList [ ( "Alice", 1 ), ( "Bob", 1 ) ]
bobAndCedd = Dict.fromList [ ( "Bob", 1 ), ( "Cedd", 1 ) ]
firstDictCountsDouble = Dict.merge
        -- when only in aliceAndBob
        (\name count mergedCounts -> Dict.insert name (2 * count) mergedCounts)
        -- when in aliceAndBob and bobAndCedd
        (\name aliceAndBobCount bobAndCeddCount mergedCounts -> Dict.insert name (2 * aliceAndBobCount + bobAndCeddCount) mergedCounts)
        -- when only in bobAndCedd
        (\name count mergedCounts -> Dict.insert name count mergedCounts)
        -- the two dicts to merge
        aliceAndBob
        bobAndCedd
        -- the initial state of the merge
        Dict.empty
--> Dict.fromList [ ( "Alice", 2 ), ( "Bob", 3 ), ( "Cedd", 1 ) ]

任何看起來會修改字典的函式或運算子(例如新增元素),實際上都會回傳一個新的字典。 效能通常不是問題,因為實作會避免不必要的記憶體配置和複製。

在 Elm 中,一般來說最好使用較高階的抽象,例如Dict.map、Dict.filter和Dict.merge,而不是Dict.get、Dict.remove這類較低階的抽象,不過這當然取決於情境。

說明

你的任務是取得一份比賽的得分球員清單,並計算每位球員攻進了多少球。

PlayerName是String的型別別名,能讓程式碼更容易閱讀,也能區分這裡使用的String與其他用途的String。

1. 彙總得分球員

首先實作updateGoalCountForPlayer,為球員初始化或累加進球數。然後實作aggregateScorers函式,透過updateGoalCountForPlayer來計算每位球員攻進了多少球。這個函式接受一個List PlayerName(得分球員的名字,可能包含重複的球員名字),並回傳一個Dict PlayerName Int,內含清單中的所有球員,以及他們各自攻進了多少球。

aggregateScorers [ "Betty", "Cedd", "Betty" ]
--> Dict.fromList [ ( "Betty", 2 ), ( "Cedd", 1 ) ]

2. 移除不重要的球員

實作removeInsignificantPlayers函式,過濾掉進球數少於門檻的球員。

removeInsignificantPlayers 2 (Dict.fromList [ ( "Betty", 2 ), ( "Cedd", 1 ) ] )
--> Dict.fromList [ ( "Betty", 2 ) ]

3. 重設球員的進球數

實作resetPlayerGoalCount函式,把某位球員的進球數重設為零。

resetPlayerGoalCount "Cedd" (Dict.fromList [ ( "Betty", 2 ), ( "Cedd", 1 ) ] )
--> Dict.fromList [ ( "Betty", 2 ), ( "Cedd", 0 ) ]

4. 格式化單一球員的進球數

實作formatPlayer函式,把單一球員的進球數格式化為字串。如果字典中沒有這位球員,就假設他的進球數為零。

formatPlayer "Betty" (Dict.fromList [ ( "Betty", 2 ), ( "Cedd", 1 ) ] )
--> "Betty: 2"

5. 格式化所有球員的進球數

實作formatPlayers函式,把所有球員的進球數格式化為字串。球員應依名字排序。

formatPlayers (Dict.fromList [ ( "Betty", 2 ), ( "Cedd", 1 ) ] )
--> "Betty: 2, Cedd: 1"

6. 合併比賽

實作combineGames函式,計算每位球員在兩場比賽中總共攻進了多少球。

combine Games
  (Dict.fromList [ ( "Betty", 2 ), ( "Cedd", 1 ) ] )
  (Dict.fromList [ ( "Betty", 2 ), ( "Mario", 3 ) ] )
--> Dict.fromList [ ( "Betty", 4 ), ( "Cedd", 1 ), ( "Mario", 3 ) ]
透過 GitHub 編輯 連結會在新視窗或分頁中開啟
Elm Exercism

準備好開始 得分王 了嗎?

註冊 Exercism,透過 28 個概念110 個練習 和真人引導來學習並精通 Elm,全部免費。