Le mappe
La ricerca di una chiave restituisce un valore oppure un errore.
Una mappa associa delle chiavi a dei valori. Il suo tipo si scrive {K: V}, dove K è il tipo delle chiavi e V quello dei valori. Una ricerca può trovare la chiave oppure fallire: il tipo del risultato rappresenta entrambi i casi.
m[k] è un V?, non un V
Scrivi la ricerca da sola e il compilatore ti dice che cosa hai in mano:
$ heroes check maps.hero
error[type_mismatch]: expected `i64`, found `i64?`
at maps.hero:9:12
|
9 | return scores[name]
| ^^^^^^^^^^^^
fix (guess): `.must()` — abort on the error case, giving `i64`
i64? è il tipo fallibile del capitolo 5: un numero intero oppure un errore. Se la chiave manca, l’errore ha codice missing_key. Prima di usare il numero devi gestire questa possibilità.
Quattro modi di gestire una ricerca
# The four ways to read a `V?`, on one line each (§4.6).
function score_of(scores: {str: i64}, name: str) -> i64
return scores[name].default(0)
.default(v) restituisce il valore trovato oppure v se la chiave manca. In questo esempio il risultato ha tipo i64.
function known(scores: {str: i64}, name: str) -> bool
return !scores[name].is_err()
.is_err() restituisce true se la chiave manca. Non estrae il valore. Per verificare che la chiave sia presente, l’esempio nega il risultato con !.
# `.is_err()` asks without extracting; the `match` extracts without aborting.
function report(scores: {str: i64}, name: str) -> str
got = scores[name]
if got.is_err()
return name + " has no score"
return name + " scored " + got.must().to_str()
.must() restituisce il valore trovato e termina il programma se la chiave manca. Qui la riga precedente ha già verificato che la chiave esiste. Usa questa forma quando hai verificato che la ricerca riesce.
# The general way, and the only one that names the error: `match` on `.ok`/`.err`.
function describe(scores: {str: i64}, name: str) -> str
return match scores[name]
.ok v => v.to_str()
.err e => e.code
match gestisce entrambi i risultati e permette di leggere anche il contenuto dell’errore. Entrambi i rami sono obbligatori.
Come se ne costruisce una
# A map is built as a literal. There is no insert in the built-in inventory
# (§4.20) — `push` is for arrays — so this table is written, not accumulated.
function table() -> {str: i64}
return {"ada": 10, "grace": 9, "alan": 8}
Puoi scrivere tutte le coppie insieme oppure dichiarare una mappa modificabile con @= e aggiungerle una alla volta. m[k] @ v inserisce il valore per la chiave k, sostituendo quello precedente se esiste. Una mappa vuota richiede il tipo esplicito: m: {str: i64} @= {}.
Una mappa non ha un ordine
keys(m) restituisce le chiavi senza un ordine garantito. L’ordine può cambiare tra due esecuzioni. Se ti serve un ordine stabile, usa sort:
w = make()
for name in sort(keys(w.rooms))
room = room_at(w, name).must()
for direction in sort(keys(room.exits))
there = room.exits[direction].must()
Questo test visita le stanze e le uscite in un ordine fisso, così l’output resta uguale a ogni esecuzione. Anche il compilatore di Heroes ha avuto un problema simile: alcune liste seguivano l’ordine delle mappe, rendendo diverse due compilazioni dello stesso programma. La correzione ha aggiunto le chiamate a sort mancanti. Il resoconto è nella sezione sul compilatore scritto in Heroes. Il confronto tra mappe non dipende invece dall’ordine: due mappe con le stesse coppie chiave-valore risultano uguali con ==.
Tutto quanto, in esecuzione
test "a missing key is a type, not a crash"
scores = table()
assert score_of(scores, "ada") == 10
assert score_of(scores, "nobody") == 0
assert known(scores, "grace")
assert known(scores, "nobody") == false
assert scores.len() == 3
function total_of(scores: {str: i64}, names: [str]) -> i64
sum: i64 @= 0
for name in names
sum @ sum + score_of(scores, name)
return sum
function main()
print(total_of(table(), ["ada", "grace", "nobody"]))
$ heroes run examples/gallery/10-maps.hero
19
Il risultato è 10 + 9 + 0. Il terzo nome non esiste nella mappa; score_of usa esplicitamente zero come valore predefinito.
Poi: Valori fino in fondo, per costruire alberi con valori indipendenti e senza puntatori. Torna all’elenco dei capitoli. ← Prima: Le stringhe
Esegui un programma tuo: bastano quattro comandi. Oppure prendi tutto il linguaggio in un file solo, e tutti i programmi già scritti.