「Pairs and Dicts」概念討論了 Julia 字典型別的基本用法。
由於Dict是重要且常用的集合型別,Julia 也提供了許多操作它們的函式。
取得項目最簡單的方式是透過鍵(就像許多語言一樣)。
不過,查詢不存在的鍵會引發KeyError。
julia> d = Dict(i => ('a':'c')[i] for i in 1:3)
Dict{Int64, Char} with 3 entries:
2 => 'b'
3 => 'c'
1 => 'a'
julia> d[2]
'b': ASCII/Unicode U+0062 (category Ll: Letter, lowercase)
# missing key
julia> d[4]
ERROR: KeyError: key 4 not found
我們可以先檢查鍵是否存在,再嘗試取得它(例如使用haskey(d, 4)),不過直接為缺少的鍵取得預設值也很有用。
為此,我們有get()函式,以及會修改字典的版本get!()。
兩者的差別在於,缺少的鍵是否會被加入Dict。
julia> get(d, 4, '!')
'!': ASCII/Unicode U+0021 (category Po: Punctuation, other)
# get() leaves Dict unchanged
julia> d
Dict{Int64, Char} with 3 entries:
2 => 'b'
3 => 'c'
1 => 'a'
julia> get!(d, 4, '!')
'!': ASCII/Unicode U+0021 (category Po: Punctuation, other)
# get!() adds new key to Dict
julia> d
Dict{Int64, Char} with 4 entries:
4 => '!'
2 => 'b'
3 => 'c'
1 => 'a'
在先前的概念中我們看到,delete!()可以依鍵移除項目。
這可以搭配取得值的動作,改用pop!()函式來完成。
julia> d
Dict{Int64, Char} with 4 entries:
4 => '!'
2 => 'b'
3 => 'c'
1 => 'a'
# popped value is returned
julia> pop!(d, 3)
'c': ASCII/Unicode U+0063 (category Ll: Letter, lowercase)
# Dict is shrunk
julia> d
Dict{Int64, Char} with 3 entries:
4 => '!'
2 => 'b'
1 => 'a'
對Dict進行疊代時,預設會產生key => value配對。
這些組成部分可以用常見的.first和.second名稱來存取。
# d is from the previous example
julia> [(kv.first, kv.second) for kv in d]
3-element Vector{Tuple{Int64, Char}}:
(4, '!')
(2, 'b')
(1, 'a')
我們經常只想使用鍵或只想使用值,這很簡單。
julia> [k for k in keys(d)]
3-element Vector{Int64}:
4
2
1
julia> [v for v in values(d)]
3-element Vector{Char}:
'!': ASCII/Unicode U+0021 (category Po: Punctuation, other)
'b': ASCII/Unicode U+0062 (category Ll: Letter, lowercase)
'a': ASCII/Unicode U+0061 (category Ll: Letter, lowercase)
合併兩個Dict可能很有用,但我們必須清楚,對於同時出現在多個輸入中的鍵,我們打算怎麼處理。
merge()和merge!()函式會以由左至右的方式合併任意數量的Dict。
既有的鍵,其值會被直接覆寫,因此最右邊的Dict「勝出」。
julia> d3 = Dict("color" => "red", "shape" => "circle")
Dict{String, String} with 2 entries:
"shape" => "circle"
"color" => "red"
julia> d4 = Dict("color" => "blue", "coords" => (3, 2))
Dict{String, Any} with 2 entries:
"color" => "blue"
"coords" => (3, 2)
# color from d3 is lost
julia> merge(d3, d4)
Dict{String, Any} with 3 entries:
"shape" => "circle"
"color" => "blue"
"coords" => (3, 2)
mergewith()和mergewith!()函式讓我們可以把函式作為第一個引數傳入,用來定義重複的鍵該如何合併。
加法就是一個簡單的例子。
julia> d5 = Dict("color" => "red", "area" => 3)
Dict{String, Any} with 2 entries:
"area" => 3
"color" => "red"
julia> d6 = Dict("shape" => "circle", "area" => 5)
Dict{String, Any} with 2 entries:
"shape" => "circle"
"area" => 5
# area values are added, not overwritten
julia> mergewith(+, d5, d6)
Dict{String, Any} with 3 entries:
"shape" => "circle"
"area" => 8
"color" => "red"
請注意,mergewith系列函式正是高階函式的絕佳範例。
Mecha Munch™ 是一家雜貨採購自動化公司,剛雇用你來開發他們的訂購應用程式。 你的團隊負責打造一個 MVP(最小可行產品),用來管理購物車的所有基本操作,讓使用者可以新增、移除和排序他們的採購訂單。 幸好,金錢和結帳功能由另一個團隊負責!
這個 MVP 應該讓使用者能把商品加入購物車。 可以是一件商品,也可以一次加入多件。 由於這是 MVP,商品數量以_重複_來表示。 如果使用者想加入 2 顆橘子,'Oranges' 會在輸入的可迭代物件中出現兩次。 如果購物車裡已經有這件商品,購物車中的數量就應該增加 1。 如果這件商品對購物車來說是_新的_,就應該以數量 1 加入。
建立函式 add_items!(<cart>, <items>),它接受一個購物車字典和一個要加入的商品可迭代物件作為引數。
它應該回傳使用者更新後的購物車字典。
julia> additems!(Dict("Banana" => 3, "Apple" => 2, "Orange" => 1),
("Apple", "Apple", "Orange", "Apple", "Banana"))
Dict{String, Int64} with 3 entries:
"Apple" => 5
"Orange" => 2
"Banana" => 4
julia> additems!(Dict("Banana" => 3, "Apple" => 2, "Orange" => 1),
["Banana", "Orange", "Blueberries", "Banana"])
Dict{String, Int64} with 4 entries:
"Blueberries" => 1
"Apple" => 2
"Orange" => 2
"Banana" => 5
這個應用程式有一個「ideas」區段,裡面放滿了來自各種菜系的現成食譜。 使用者可以任選其中一份食譜,把它的所有食材自動加入購物車。 專案經理請你建立一種方式來編輯這些「ideas」食譜,因為內容團隊一直在調整食材和數量。
建立函式 update_recipes!(<ideas>, <updates>),它接受一個「ideas」字典和一個食譜更新的字典作為引數。
這個函式應該回傳更新後的「ideas」字典。
julia>update_recipes!(
Dict("Banana Bread" => Dict("Banana" => 1, "Apple" => 1, "Walnuts" => 1, "Flour" => 1, "Eggs" => 2, "Butter" => 1),
"Raspberry Pie" => Dict("Raspberry" => 1, "Orange" => 1, "Pie Crust" => 1, "Cream Custard" => 1)),
Dict("Banana Bread" => Dict("Banana" => 4, "Walnuts" => 2, "Flour" => 1, "Butter" => 1, "Milk" => 2, "Eggs" => 3))
)
Dict{String, Dict{String, Int64}} with 2 entries:
"Raspberry Pie" => Dict("Cream Custard"=>1, "Raspberry"=>1, "Orange"=>1, "Pie Crust"=>1)
"Banana Bread" => Dict("Eggs"=>3, "Flour"=>1, "Milk"=>2, "Butter"=>1, "Walnuts"=>2, "Banana"=>4)
julia> update_recipes!(
Dict("Banana Bread" => Dict("Banana" => 1, "Apple" => 1, "Walnuts" => 1, "Flour" => 1, "Eggs" => 2, "Butter" => 1),
"Raspberry Pie" => Dict("Raspberry" => 1, "Orange" => 1, "Pie Crust" => 1, "Cream Custard" => 1),
"Pasta Primavera" => Dict("Eggs" => 1, "Carrots" => 1, "Spinach" => 2, "Tomatoes" => 3, "Parmesan" => 2, "Milk" => 1, "Onion" => 1)),
Dict("Raspberry Pie" => Dict("Raspberry" => 3, "Orange" => 1, "Pie Crust" => 1, "Cream Custard" => 1, "Whipped Cream" => 2),
"Pasta Primavera" => Dict("Eggs" => 1, "Mixed Veggies" => 2, "Parmesan" => 2, "Milk" => 1, "Spinach" => 1, "Bread Crumbs" => 1),
"Blueberry Crumble" => Dict("Blueberries" => 2, "Whipped Creme" => 2, "Granola Topping" => 2, "Yogurt" => 3))
)
Dict{String, Dict{String, Int64}} with 4 entries:
"Raspberry Pie" => Dict("Raspberry"=>3, "Orange"=>1, "Pie Crust"=>1, "Cream Custard"=>1, "Whipped Cream"=>2)
"Pasta Primavera" => Dict("Eggs"=>1, "Mixed Veggies"=>2, "Parmesan"=>2, "Milk"=>1, "Spinach"=>1, "Bread Crumbs"=>1)
"Blueberry Crumble" => Dict("Blueberries"=>2, "Whipped Creme"=>2, "Granola Topping"=>2, "Yogurt"=>3)
"Banana Bread" => Dict("Banana"=>1, "Apple"=>1, "Walnuts"=>1, "Flour"=>1, "Eggs"=>2, "Butter"=>1)
這個應用程式需要把指定使用者的購物車送往商店以便履行訂單。 不過,商店裡的揀貨人員需要透過唯一的走道代碼知道商品在哪裡。 「fulfillment cart」需要依照商品的走道代碼排序,讓揀貨人員更有效率。
建立函式 send_to_store(<cart>, <aislecodes>),它接受一個使用者購物車,以及一個含有商店走道代碼的字典。
這個函式應該回傳一個合併後的「fulfillment cart」,其中為顧客訂購的每件商品各有一組 aislecode => quantity 配對。
商品應該依走道代碼的順序排列。
julia> cart = Dict("Orange" => 1, "Milk" => 2, "Banana" => 3, "Apple" => 2, "Chocolate" => 2);
julia> aislecodes = Dict("Orange" => 2.4, "Milk" => 3.3, "Banana" => 2.6, "Apple" => 2.2, "Chocolate" => 5.7);
julia> send_to_store(cart, aislecodes)
5-element Vector{Pair{Float64, Int64}}:
2.2 => 2
2.4 => 1
2.6 => 3
3.3 => 2
5.7 => 2
這個應用程式不能只是一直下顧客的訂單。 總有一天,商店的各種商品會用完。 所以你的應用程式 MVP 在每次使用者把訂單送往商店時,都需要更新商店庫存。 否則,顧客會訂購實際上已經沒有庫存的商品。
建立函式 update_store_inventory!(<inventory>, <cart>),它接受一個 cart 和一個 inventory。
這個函式應該依照 cart 中訂購的數量減少商店庫存,然後回傳一個字典,其中包含所有缺貨的商品(也就是數量已到 0 的商品)。
julia> inventory = Dict("Banana" => 15, "Apple" => 12, "Orange" => 1, "Milk" => 4);
julia> cart = Dict("Orange" => 1, "Milk" => 2, "Banana" => 3, "Apple" => 2);
julia> update_store_inventory!(inventory, cart)
Dict{String, Int64} with 1 entry:
"Orange" => 0
julia> inventory
Dict{String, Int64} with 4 entries:
"Milk" => 2
"Apple" => 10
"Orange" => 0
"Banana" => 12
除了更新庫存,你的應用程式還能協助補貨。 你的 MVP 應該要能在顧客下單後,為任何缺貨的商品準備一筆小型訂單。 這有助於讓觸發一筆大量訂單變得更簡單。 此外,有時候會有不在庫存清單上的限時促銷商品。 如果這些商品在促銷期結束前就售完,代表它們很受歡迎,可以加入庫存清單。
建立 reorder!(<outofstock>, <stock>),它接受 outofstock 和 stock。
stock 字典包含標準庫存商品及其重新訂購數量。
outofstock 字典是 update_store_inventory! 的結果,也就是一份數量為零的商品字典。
outofstock 的數量應該更新為 stock 中的數量,然後回傳。
此外,outofstock 中任何不在 stock 裡的商品,都應該以預設數量 100 加入 stock。
julia> inventory = Dict("Banana" => 15, "Apple" => 12, "Orange" => 1, "Milk" => 4, "Chocolate" => 2);
julia> cart = Dict("Orange" => 1, "Milk" => 2, "Banana" => 3, "Apple" => 2, "Chocolate" => 2);
julia> stock = Dict("Banana" => 150, "Apple" => 120, "Orange" => 120, "Milk" => 45);
julia> outofstock = update_store_inventory!(inventory, cart)
Dict{String, Int64} with 1 entry:
"Orange" => 0
"Chocolate" => 0
julia> reorder!(outofstock, stock)
Dict{String, Int64} with 1 entry:
"Orange" => 120
"Chocolate" => 100
julia> stock
Dict{String, Int64} with 5 entries:
"Milk" => 45
"Apple" => 120
"Chocolate" => 100
"Orange" => 120
"Banana" => 150