Go-giocare con le mappe

Sommario

Utili esercizi in linguaggio Go con le mappe.

Come fareste a…

…fare il conteggio di quanti oggetti ci sono sulla scrivania?
Per prima cosa dovremo pensare ad una rappresentazione dei dati. Decidiamo per la più semplice, ovvero per un elenco di parole: quando troviamo un oggetto ne riportiamo il nome nell’elenco.

In Go la cosa più naturale per rappresentare un elenco di parole è utilizzare il tipo slice: si tratta di una sorta di finestra su un array, che in Go sono tipi in cui la lunghezza non solo è non modificabile ma fa parte del tipo stesso.

Quello che noto è che i progettisti del linguaggio fanno di tutto per associare i dati ad un tipo, tipizzazione forte, e su questa base vogliono mettere a disposizione strutture agili ed efficienti. Questo significa proporre un linguaggio con la robustezza del C++ ma con la facilità di Python, ed è solo uno degli aspetti del carattere equilibrato del Go…

// creazione di uno slice di stringhe con la
// sintassi letterale: esprimo direttamente i valori
var things = []string{"BOOK","CALENDAR","MANUAL","ECC"}

Stampiamo subito l’elenco!

Per stampare l’elenco è sufficiente iterare sullo slice. Ecco il programma completo:

package main

import "fmt"

var things = []string{"BOOK",
    "CALENDAR",
    "MANUAL",
    "BOOK",
    "ECC"}

func main(){
    for _, t := range things {
        fmt.Println(t)
    }
}

Le mappe

Per contare gli oggetti possiamo usare una mappa, ovvero un dizionario di chiavi/valori già disponibile nel linguaggio. Le chiavi saranno i nomi degli oggetti ed i valori associati saranno le quantità. Creiamo subito una mappa del genere che dobbiamo inizializzare con make():

// a new map...
var inv map[string]int
inv = make(map[string]int)

// or in one declaration line
var inv = make(map[string]int)

Al solito, siamo obbligati a definire con esattezza sia il tipo della chiave che quello del valore, ma il fatto che le mappe sono oggetti interni al linguaggio Go, ovvero come si dice più sinteticamente built-in, fa pensare che si sia voluto facilitarne l’uso e, con la filosofia dell’equilibrio del robusto fa semplice e divertente, possiamo crearla sia per via letterale che usando la dichiarazione breve:

// empty literal map definition
var inv = map[string]int{}

// short declaration syntax
inv := map[string]int{}

// or
inv := make(map[string]int)

Il procedimento per elaboare l’elenco è questo: per ogni nome di oggetto creiamo una chiave a cui assegnamo il valore 1, se la chiave esiste già, prendiamo il valore attuale e lo incrementiamo di 1. Ecco il programma d’esempio completo:

package main

import "fmt"

var things = []string{"BOOK","CALENDAR","MANUAL","BOOK","ECC"}

func main(){
    inv := make(map[string]int)
    for _, t := range things {
        _, found := inv[t]
        if !found { // ! is the NOT logic operator
            inv[t] = 1
        } else {
            inv[t] = inv[t] + 1
        }
    }
    fmt.Println(inv)
}

Oltre alla sintassi per accedere ai campi del dizionario, di nuovo c’é l’espressione alla riga 10: inv[t] restituisce il valore associato alla chiave t ed il valore booleano vero o falso a seconda che la chiave risulti o meno già definita.
Poiché non sono ammesse variabili istanziate ma poi non utilizzate, occorre inserire il blanck identifier ovvero il trattino basso al nome della variabile che restituisce il valore, ovvero la prima.

Notiamo anche che la funzione Println() del pacchetto fmt stampa qualsiasi cosa gli sia data in pasto, numeri, slice, e mappe in questo caso.

Sempre più idiomatic Go

Le prime modifiche al listato precedente sono l’assegnazione del flag found all’interno dell’if così che sia definito solo quando serve, e l’uso dell’operatore di incremento:

package main

import "fmt"

var things = []string{"BOOK","CALENDAR","MANUAL","BOOK","ECC"}

func main(){
    inv := make(map[string]int)
    for _, t := range things {
        if _, found := inv[t]; !found {
            inv[t] = 1
        } else {
            inv[t] += 1
        }
    }
    fmt.Println(inv)
}

Già più compatto ed idiomaticoso, ma possiamo fare ancora meglio:

package main

import "fmt"

// "pretty printing" of an inventory
func printInventory(data map[string]int) {
    for thing, num := range data {
        fmt.Println(num, thing)
    }
}

func main() {
    inv := make(map[string]int)
    for _, t := range things {
        inv[t]++
    }

    printInventory(inv)
}

// a new slice of things that I founded on my desk
var things = []string{
    "PENCIL", "BOOK", "BAG", "BOOK", "BOOK", "BOOK",
    "BAG", "NEWSPAPER", "MANUAL", "CALENDAR","BAG",
    "CANDLE", "BOTTLE", "PENCIL", "PAPER","GLASSES",
    "NEWSPAPER", "PAPER","MANUAL","PHOTOGRAPH","MUG",
}

/*
$ go run ex2.go
4 BOOK
1 CANDLE
3 BAG
1 BOTTLE
1 PHOTOGRAPH
2 PAPER
1 GLASSES
1 CALENDAR
2 PENCIL
1 MUG
2 MANUAL
2 NEWSPAPER
$ Exit code: 0
*/

Ho tolto l’if e continua a funzionare!
In altri linguaggi come per esempio Lua, non possiamo assegnare un valore ad una chiave che non esiste ancora, mentre in Go si. In Lua le variabili non vengono inizializzate se non nel momento dell’assegnazione di un valore. Il tipo viene determinato all’istante dal compilatore solo quando il valore è determinato. In Go è il contrario: il compilatore conosce già il tipo ed inizializza le variabili al corrispondente valore nullo.

Ora, anche in Go a favore della filosofia del robusto ma intuitivo, è possibile creare variabili con la determinazione del tipo fatta dal compilatore, ma quando viene creato un elemento della mappa il valore della chiave è inizializzato al valore nullo che per gli interi è zero, così posso immediatamente incrementarlo.

Nel codice ho aggiunto una nuova funzione che stampa l’inventario in colonna. Notare che si è liberi di inserire il codice nell’ordine che si preferisce: nell’esempio la funzione di stampa compare prima che venga utilizzata in main(), mentre succede il contrario per la definizione globale dello slice things.
L’output del programma è riportato alla fine del listato all’interno di un commento multiriga (in Go i commenti sono identici a quelli del C++ ed anche questa somiglianza non è un caso…).

Go-nsigli

Per imparare un linguaggio è necessario scrivere programmi di esercizio. Va bene seguire un buon libro, ma è indispensabile ripetere gli esempi ridigitandoli o addirittura riscrivendoli con carta e matita. Solo così si comincia a pensare nel nuovo linguaggio.
Non è necessario installare nulla, basta recarsi sul sito Playground per essere pronti a digitare ed eseguire il codice.

Ripassate anche gli altri post tematici di Juhan sul Go apparsi sul blog, specie quelli sugli slice e sulle mappe

A presto…
R.

Posta un commento o usa questo indirizzo per il trackback.

Rispondi

Inserisci i tuoi dati qui sotto o clicca su un'icona per effettuare l'accesso:

Logo di WordPress.com

Stai commentando usando il tuo account WordPress.com. Chiudi sessione /  Modifica )

Google photo

Stai commentando usando il tuo account Google. Chiudi sessione /  Modifica )

Foto Twitter

Stai commentando usando il tuo account Twitter. Chiudi sessione /  Modifica )

Foto di Facebook

Stai commentando usando il tuo account Facebook. Chiudi sessione /  Modifica )

Connessione a %s...

Questo sito utilizza Akismet per ridurre lo spam. Scopri come vengono elaborati i dati derivati dai commenti.

%d blogger hanno fatto clic su Mi Piace per questo: