Il concetto Pairs and Dicts ha trattato le basi del tipo dizionario di Julia.
Poiché i Dict sono un tipo di collezione importante e molto usato, esistono anche molte funzioni per manipolarli.
Il modo più semplice per recuperare una voce è tramite la chiave (come in molti linguaggi).
Tuttavia, chiedere una chiave inesistente provoca un 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
Potremmo verificare l'esistenza della chiave prima di provare a recuperarla (con qualcosa come haskey(d, 4)), ma può essere utile ottenere direttamente un valore predefinito per le chiavi mancanti.
Per questo abbiamo la funzione get() e la sua variante mutante get!().
La differenza sta nel fatto che una chiave «mancante» venga aggiunta o meno al 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'
Nel concetto precedente abbiamo visto che delete!() rimuove una voce tramite la chiave.
Questo si può combinare con il recupero del valore usando la funzione 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'
Iterare su un Dict restituisce di default coppie key => value.
Le componenti sono accessibili con la solita nomenclatura .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')
Spesso vogliamo usare solo le chiavi o solo i valori, e la cosa è semplice.
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)
Combinare due Dict può essere utile, ma dobbiamo chiarire cosa vogliamo fare con le chiavi presenti in più di un input.
Le funzioni merge() e merge!() combinano un numero arbitrario di Dict forniti, da sinistra a destra.
I valori delle chiavi esistenti vengono semplicemente sovrascritti, quindi il Dict più a destra «vince».
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)
Le funzioni mergewith() e mergewith!() ci permettono di passare una funzione come primo argomento, per definire come devono essere unite le chiavi ripetute.
L'addizione è un semplice esempio.
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"
Nota che le funzioni mergewith sono un buon esempio di Higher Order Functions in azione.
Mecha Munch™, un'azienda di automazione per la spesa, ti ha appena assunto per lavorare sulla sua app per gli ordini. Il tuo team ha il compito di costruire un MVP (prodotto minimo funzionante) che gestisce tutte le attività di base del carrello della spesa, permettendo agli utenti di aggiungere, rimuovere e ordinare i loro ordini di generi alimentari. Per fortuna, un altro team si occupa di tutto ciò che riguarda il denaro e il check-out!
L'MVP dovrebbe permettere all'utente di aggiungere articoli al proprio carrello. Può trattarsi di un singolo articolo o di più articoli contemporaneamente. Poiché si tratta di un MVP, la quantità di un articolo è indicata dalle ripetizioni. Se un utente vuole aggiungere 2 arance, 'Oranges' comparirà due volte nell'iterabile di input. Se l'utente ha già l'articolo nel carrello, la quantità nel carrello va aumentata di 1. Se l'articolo è nuovo per il carrello, va aggiunto con una quantità di 1.
Definisci la funzione add_items!(<cart>, <items>) che prende come argomenti un dizionario del carrello e un iterabile di articoli da aggiungere.
Dovrebbe restituire il dizionario del carrello aggiornato per l'utente.
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
L'app ha una sezione "idee" piena di ricette finite di varie cucine. L'utente può selezionare una qualsiasi di queste ricette e far aggiungere automaticamente tutti i suoi ingredienti al proprio carrello. Il project manager ti ha chiesto di creare un modo per modificare queste ricette della sezione "idee", dato che il team di contenuti continua a cambiare ingredienti e quantità.
Definisci la funzione update_recipes!(<ideas>, <updates>) che prende come argomenti un dizionario "idee" e un dizionario di aggiornamenti alle ricette.
La funzione dovrebbe restituire il dizionario "idee" aggiornato.
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)
L'app deve inviare il carrello di un utente al negozio per l'evasione. Tuttavia, i commessi del negozio devono sapere dove si trova ogni articolo, usando un codice di corsia univoco. Il "carrello di evasione" va ordinato in base ai codici di corsia degli articoli, per aiutare i commessi a essere più efficienti.
Definisci la funzione send_to_store(<cart>, <aislecodes>) che prende come argomenti il carrello di un utente e un dizionario con i numeri di corsia del negozio.
La funzione dovrebbe restituire un "carrello di evasione" che ha una coppia aislecode => quantity per ogni articolo ordinato dal cliente.
Gli articoli devono comparire nell'ordine dei loro codici di corsia.
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
L'app non può semplicemente registrare ordini dei clienti all'infinito. Prima o poi, al negozio finiranno vari prodotti. Quindi l'MVP dell'app deve aggiornare l'inventario del negozio ogni volta che un utente invia il proprio ordine al negozio. Altrimenti, i clienti ordineranno prodotti che in realtà non sono disponibili.
Definisci la funzione update_store_inventory!(<inventory>, <cart>) che prende un cart e un inventory.
La funzione dovrebbe ridurre le quantità dell'inventario del negozio del numero «ordinato» nel carrello e poi restituire un dizionario degli articoli esauriti (cioè quelli che hanno raggiunto 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
Oltre ad aggiornare l'inventario, l'app può aiutare a rifornirlo. L'MVP dovrebbe essere in grado di preparare un mini-ordine degli articoli esauriti dopo l'ordine di un cliente. Così sarà più semplice avviare un ordine più grande. Inoltre, a volte ci sono articoli in promozione a tempo limitato che non sono nella lista delle scorte. Se si esauriscono prima della fine del periodo promozionale, significa che hanno avuto successo e possono essere aggiunti alla lista delle scorte.
Definisci la funzione reorder!(<outofstock>, <stock>) che prende outofstock e stock.
Il dizionario stock contiene gli articoli standard e le rispettive quantità di riordino.
Il dizionario outofstock è il risultato di update_store_inventory!, cioè un dizionario di articoli con quantità zero.
Le quantità in outofstock vanno aggiornate con quelle in stock e restituite.
Inoltre, qualsiasi articolo presente in outofstock ma non in stock va aggiunto a stock con una quantità predefinita di 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
Iscriviti a Exercism per imparare e padroneggiare Julia con 35 concetti128 esercizi e il mentoring di persone reali, tutto gratis.