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的这种用法与它的其他用法区分开。
首先实现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 ) ]