Percursos
/
Julia
Julia
/
Exercícios
/
Gestão do Mecha Munch
Gestão do Mecha Munch

Gestão do Mecha Munch

Exercício de aprendizagem

Introdução

O conceito Pares e Dicionários abordou as bases do tipo dicionário da Julia.

Como os Dict são um tipo de coleção importante e muito utilizado, há também muitas funções para os manipular.

Obter entradas

A forma mais simples de obter uma entrada é pela chave (como em muitas linguagens). No entanto, pedir uma chave que não existe resulta num 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

Poderíamos testar a chave antes de tentar obtê-la (com algo como haskey(d, 4)), mas pode ser útil obter simplesmente um valor predefinido para as chaves em falta.

Para isso, temos a função get() e a sua variante mutável get!(). Estas diferem quanto ao facto de uma chave «em falta» ser ou não adicionada ao 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'

Vimos no conceito anterior que delete!() remove uma entrada pela chave. Isto pode ser combinado com a obtenção do valor, usando a função 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'

Iteração

Iterar sobre um Dict produz, por predefinição, pares key => value. Os componentes estão acessíveis com a nomenclatura habitual .first e .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')

Muitas vezes, queremos usar apenas as chaves ou apenas os valores, o que é fácil.

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)

Combinar

Combinar dois Dict pode ser útil, mas temos de ser claros quanto ao que pretendemos fazer com as chaves que estão presentes em mais do que um dos dicionários de entrada.

As funções merge() e merge!() combinam um número arbitrário de Dict fornecidos, da esquerda para a direita.

Os valores das chaves existentes são simplesmente substituídos, por isso o Dict mais à direita «ganha».

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)

As funções mergewith() e mergewith!() permitem-nos fornecer uma função como primeiro argumento, para definir como devem ser combinadas as chaves repetidas. A adição é um exemplo simples.

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"

Repara que as funções mergewith são um bom exemplo de funções de ordem superior em ação.

Instruções

A Mecha Munch™, uma empresa de automatização de compras de supermercado, acabou de te contratar para trabalhares na sua aplicação de encomendas. A tua equipa tem a tarefa de criar um MVP (produto mínimo viável) que gere todas as atividades básicas do carrinho de compras, permitindo aos utilizadores adicionar, remover e ordenar as suas encomendas de supermercado. Felizmente, há outra equipa a tratar de tudo o que diz respeito ao dinheiro e ao pagamento!

1. Adiciona artigo(s) ao carrinho de compras do utilizador

O MVP deve permitir que o utilizador adicione artigos ao seu carrinho de compras. Pode ser um único artigo ou vários artigos de uma só vez. Como se trata de um MVP, a quantidade de cada artigo é indicada por repetições. Se um utilizador quiser adicionar 2 Oranges, «Oranges» aparece duas vezes no iterável de entrada. Se o utilizador já tiver o artigo no carrinho, a quantidade no carrinho deve ser aumentada em 1. Se o artigo for novo no carrinho, deve ser adicionado com uma quantidade de 1.

Cria a função add_items!(<cart>, <items>) que recebe um dicionário que representa o carrinho e um iterável de artigos a adicionar como argumentos. Deve devolver o dicionário do carrinho de compras atualizado do utilizador.

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. Atualiza a secção «ideias» de receitas

A aplicação tem uma secção «ideias» preenchida com receitas completas de várias cozinhas. O utilizador pode selecionar qualquer uma destas receitas e fazer com que todos os seus ingredientes sejam adicionados automaticamente ao carrinho de compras. O responsável pelo projeto pediu-te para criares uma forma de editar estas receitas «ideias», uma vez que a equipa de conteúdos está sempre a alterar ingredientes e quantidades.

Cria a função update_recipes!(<ideas>, <updates>) que recebe um dicionário «ideias» e um dicionário de atualizações de receitas como argumentos. A função deve devolver o dicionário «ideias» atualizado.

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. Envia o carrinho de compras do utilizador para a loja, para preparação

A aplicação precisa de enviar o carrinho de um determinado utilizador para a loja, para preparação. No entanto, quem prepara as encomendas na loja precisa de saber onde encontrar cada artigo, através de um código de corredor único. O «carrinho de preparação» tem de ser ordenado pela ordem dos códigos de corredor dos artigos, para ajudar quem prepara as encomendas a ser mais eficiente.

Cria a função send_to_store(<cart>, <aislecodes>) que recebe o carrinho de compras de um utilizador e um dicionário com os números de corredor da loja. A função deve devolver um «carrinho de preparação» combinado, com um par aislecode => quantity para cada artigo que o cliente está a encomendar. Os artigos devem aparecer pela ordem dos respetivos códigos de corredor.

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. Atualiza o inventário da loja de acordo com o que o utilizador encomendou

A aplicação não pode limitar-se a fazer encomendas de clientes indefinidamente. Mais cedo ou mais tarde, a loja vai ficar sem vários produtos. Por isso, o MVP da tua aplicação precisa de atualizar o inventário da loja sempre que um utilizador envia a sua encomenda para a loja. Caso contrário, os clientes vão encomendar produtos que na verdade não estão disponíveis.

Cria a função update_store_inventory!(<inventory>, <cart>) que recebe um cart e um inventory. A função deve reduzir as quantidades do inventário da loja pelo número «encomendado» no «carrinho» e depois devolver um dicionário com os artigos que estão esgotados (ou seja, que chegaram a 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. Encomenda de novo os artigos esgotados

Além de atualizar o inventário, a tua aplicação também pode ajudar a reabastecê-lo. O teu MVP deve conseguir preparar uma mini-encomenda de todos os artigos que ficam esgotados depois da encomenda de um cliente. Isto vai ajudar a simplificar o lançamento de uma encomenda maior. Além disso, por vezes há artigos promocionais por tempo limitado que não estão na lista de stock. Se se esgotarem antes de terminar o período promocional, é sinal de que são populares e podem ser adicionados à lista de stock.

Cria a função reorder!(<outofstock>, <stock>) que recebe outofstock e stock. O dicionário stock contém os artigos de stock habituais e as respetivas quantidades de reencomenda. O dicionário outofstock é o resultado de update_store_inventory!, ou seja, um dicionário com os artigos de quantidade zero. As quantidades de outofstock devem ser atualizadas de acordo com as de stock e devolvidas. Além disso, os artigos de outofstock que não estejam presentes em stock devem ser adicionados a stock com uma quantidade predefinida de 100.

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
Editar via GitHub A ligação abre numa nova janela ou separador
Julia Exercism

Estás pronto para começar Gestão do Mecha Munch?

Inscreve-te no Exercism para aprenderes e dominares Julia com 35 conceitos128 exercícios, e mentoria humana real, tudo grátis.