در 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 بهروزرسانی کرد.
اگر کلید وجود نداشته باشد، تابع 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.get و Dict.remove از انتزاعهای سطح بالا مثل Dict.map، Dict.filter و Dict.merge استفاده کنید، هرچند البته این موضوع به بافت بستگی دارد.
وظیفهی شما این است که فهرستی از گلزنان یک بازی را بگیرید و بشمارید که هر بازیکن چند گل زده است.
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 ) ]