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。
首先實作updateGoalCountForPlayer,為球員初始化或累加進球數。然後實作aggregateScorers函式,透過updateGoalCountForPlayer來計算每位球員攻進了多少球。這個函式接受一個List PlayerName(得分球員的名字,可能包含重複的球員名字),並回傳一個Dict PlayerName Int,內含清單中的所有球員,以及他們各自攻進了多少球。
aggregateScorers [ "Betty", "Cedd", "Betty" ]
--> Dict.fromList [ ( "Betty", 2 ), ( "Cedd", 1 ) ]
實作removeInsignificantPlayers函式,過濾掉進球數少於門檻的球員。
removeInsignificantPlayers 2 (Dict.fromList [ ( "Betty", 2 ), ( "Cedd", 1 ) ] )
--> Dict.fromList [ ( "Betty", 2 ) ]
實作resetPlayerGoalCount函式,把某位球員的進球數重設為零。
resetPlayerGoalCount "Cedd" (Dict.fromList [ ( "Betty", 2 ), ( "Cedd", 1 ) ] )
--> Dict.fromList [ ( "Betty", 2 ), ( "Cedd", 0 ) ]
實作formatPlayer函式,把單一球員的進球數格式化為字串。如果字典中沒有這位球員,就假設他的進球數為零。
formatPlayer "Betty" (Dict.fromList [ ( "Betty", 2 ), ( "Cedd", 1 ) ] )
--> "Betty: 2"
實作formatPlayers函式,把所有球員的進球數格式化為字串。球員應依名字排序。
formatPlayers (Dict.fromList [ ( "Betty", 2 ), ( "Cedd", 1 ) ] )
--> "Betty: 2, Cedd: 1"
實作combineGames函式,計算每位球員在兩場比賽中總共攻進了多少球。
combine Games
(Dict.fromList [ ( "Betty", 2 ), ( "Cedd", 1 ) ] )
(Dict.fromList [ ( "Betty", 2 ), ( "Mario", 3 ) ] )
--> Dict.fromList [ ( "Betty", 4 ), ( "Cedd", 1 ), ( "Mario", 3 ) ]