مسیرها
/
Julia
Julia
/
تمرین‌ها
/
مدیریت خوراک مکا
مدیریت خوراک مکا

مدیریت خوراک مکا

تمرین یادگیری

مقدمه

در مفهوم «جفت‌ها و دیکشنری‌ها» مبانی نوع دیکشنری در Julia بررسی شد.

از آنجا که Dictها یک نوع داده‌ی مجموعه‌ای مهم و پرکاربرد هستند، توابع زیادی هم برای کار با آن‌ها وجود دارد.

گرفتن درایه‌ها

ساده‌ترین راه برای بازیابی یک درایه، استفاده از کلید است (مثل بسیاری از زبان‌ها). اما درخواست کلیدی که وجود ندارد، منجر به 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 است، تعداد هر قلم با تکرار نشان داده می‌شود. اگر کاربر بخواهد ۲ پرتقال اضافه کند، 'Oranges' دو بار در iterable ورودی ظاهر می‌شود. اگر کاربر از قبل این قلم را در سبدش داشته باشد، تعداد آن در سبد یک واحد افزایش می‌یابد. اگر قلم برای سبد جدید باشد، باید با تعداد ۱ اضافه شود.

تابع add_items!(<cart>, <items>) را بسازید که یک دیکشنری سبد خرید و یک iterable از اقلام برای افزودن را به عنوان آرگومان می‌گیرد. این تابع باید دیکشنری به‌روزشده‌ی سبد خرید کاربر را برگرداند.

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. به‌روزرسانی بخش «ایده‌ها»ی دستور پخت

اپلیکیشن بخشی به اسم «ایده‌ها» دارد که پر از دستورهای پخت آماده از آشپزی‌های گوناگون است. کاربر می‌تواند هر یک از این دستورها را انتخاب کند و همه‌ی مواد لازم آن به‌طور خودکار به سبد خریدش اضافه شود. مدیر پروژه از شما خواسته است راهی برای ویرایش این دستورهای «ایده‌ها» بسازید، چون تیم محتوا مدام مواد و مقدارها را تغییر می‌دهد.

تابع update_recipes!(<ideas>, <updates>) را بسازید که یک دیکشنری «ایده‌ها» و یک دیکشنری از به‌روزرسانی‌های دستور پخت را به عنوان آرگومان می‌گیرد. این تابع باید دیکشنری «ایده‌ها»ی به‌روزشده را برگرداند.

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. ارسال سبد خرید کاربر به فروشگاه برای انجام سفارش

اپلیکیشن باید سبد خرید یک کاربر مشخص را برای انجام سفارش به فروشگاه بفرستد. اما کسانی که در فروشگاه سفارش‌ها را آماده می‌کنند باید بدانند هر قلم را با یک کد راهروی یکتا کجا می‌توان پیدا کرد. «سبد انجام سفارش» باید بر اساس کد راهروی اقلام مرتب شود تا کار آماده‌سازی سریع‌تر پیش برود.

تابع send_to_store(<cart>, <aislecodes>) را بسازید که یک سبد خرید کاربر و یک دیکشنری شامل شماره‌ی راهروهای فروشگاه را می‌گیرد. این تابع باید یک «سبد انجام سفارش» ترکیبی برگرداند که برای هر قلم سفارش‌داده‌شده‌ی مشتری یک جفت 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 ثبت‌نام کنید تا Julia را همراه با 35 مفهوم128 تمرین و مربی‌گری انسانی واقعی یاد بگیرید و در آن استاد شوید، همه‌ی این‌ها رایگان.