學習軌道
/
Julia
Julia
/
練習
/
機甲零食管理
機甲零食管理

機甲零食管理

學習練習

簡介

「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(最小可行產品),用來管理購物車的所有基本操作,讓使用者可以新增、移除和排序他們的採購訂單。 幸好,金錢和結帳功能由另一個團隊負責!

1. 將商品加入使用者的購物車

這個 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

2. 更新食譜「ideas」區段

這個應用程式有一個「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)

3. 將使用者的購物車送往商店以便履行訂單

這個應用程式需要把指定使用者的購物車送往商店以便履行訂單。 不過,商店裡的揀貨人員需要透過唯一的走道代碼知道商品在哪裡。 「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

4. 更新商店庫存以反映使用者的訂購內容

這個應用程式不能只是一直下顧客的訂單。 總有一天,商店的各種商品會用完。 所以你的應用程式 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

5. 重新訂購缺貨商品

除了更新庫存,你的應用程式還能協助補貨。 你的 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
透過 GitHub 編輯 連結會在新視窗或分頁中開啟
Julia Exercism

準備好開始 機甲零食管理 了嗎?

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