giovedì 19 marzo 2015

Corso Lua - puntata 3 - La tabella


La tabella

In questa puntata del corso base su Lua, parleremo della tabella l'unico tipo strutturato predefinito di Lua. Diamone subito la definizione.

La tabella è un dizionario cioè l'insieme non ordinato di coppie chiavi/valore. In Lua ne è previsto l'uso molteplice: se le chiavi sono numeri interi la tabella fa le veci di un array, se le chiavi sono di altro tipo essa sarà un dizionario, ovvero un array associativo.

Le chiavi possono essere di tutti i tipi previsti da Lua tranne che il tipo nil. I valori possono appartenere a qualsiasi tipo. Nulla vieta che in una stessa tabella coesistano chiavi di tipo diverso.

Dal punto di vista sintattico, una tabella di Lua è un oggetto racchiuso tra parentesi graffe. La più semplice tabella è quella vuota che si crea così:
  t = {} -- una tabella

Per assegnare e ottenere il valore associato a una chiave si utilizzano le parentesi quadre, ecco un esempio (svolgetelo in modalità interattiva avviando l'interprete Lua in un terminale dando il comando 'lua'):
  t = {}
  t["key"] = "val"

  print(t["key"]) --> stampa "val"

Stando alla definizione che abbiamo dato una tabella può avere chiavi anche di tipo differente, e infatti è proprio così e ciò vale anche per i valori. In questo esempio una tabella ha chiavi di tipo numerico e di tipo stringa con valori a loro assegnati a sua volta di tipo diverso:
  t = {}
  t["key"] = 123
  t[123] = "key"

  print(t["key"]) --> stampa il tipo numerico 123
  print(t["123])  --> stampa il tipo stringa "key"

La tabella è un oggetto

Proseguiamo nel presentare i concetti di base del linguaggio riservando alle prossime puntate del corso la scrittura vera e propria di codice applicativo.

Cosa significa che la tabella di Lua è un oggetto come abbiamo scritto nel titolo di sezione? Vuol dire che la tabella è un'entità in memoria gestita con riferimenti.

In conseguenza, se si copia un riferimento a tabella in una seconda variabile ci si riferirà alla stessa tabella e non a una sua copia:
  t = {}
  t[1] = 10
  t[2] = 20

  other = t
  t[1] = t[1] + t[2]
  assert(other[1] == 30 ) -- other e t si riferiscono alla stessa tabella
  assert(t[1] == 30)

Questa proprietà della tabella di Lua è la premessa fondamentale per la programmazione a oggetti in Lua e per scrivere codice più compatto nelle elaborazioni su tabelle molto complesse.

In effetti possiamo memorizzare in una tabella ulteriori tabelle assegnandole come valore di chiavi stringa e strutturare con complessità arbitraria i dati. In altri termini una tabella può rappresentare una struttura ad albero senza limiti teorici.
Poiché la tabella è gestita attraverso un riferimento come appunto un oggetto, nell'albero di tabelle vi saranno solamente i corrispondenti riferimenti e non le tabelle come valore stivate in qualche area di memoria.

Il costrutture e la 'dot notation'

Dunque la tabella è un tipo di dato molto flessibile (e sufficientemente efficiente). Può essere usata in moltissime diverse situazioni ed essa è ancora più utile grazie all'efficacia del suo costruttore.

Ispirato al formato di dati bibliografici di BibTeX, uno dei programmi storici del sistema TeX per la gestione delle bibliografie nei documenti LaTeX, il costruttore di Lua può creare tabelle da una sequenza di chiavi/valori in questo modo:
  t = { a = 123, b = 456, c = "valore"}

La chiave appare come il nome di una variabile ma in realtà nel costruttore essa viene interpretata come una chiave di tipo stringa. Così l'esempio precedente è equivalente al seguente codice:
  -- codice equivalente
  t = {}
  t["a"] = 123
  t["b"] = 456
  t["c"] = "valore"

La notazione del costruttore non ammette l'utilizzo diretto di chiavi numeriche. Se occorrono è necessario utilizzare le parentesi quadre per racchiudere il numero:
  -- chiavi numeriche nel costruttore?
  t_error = { 1 = 123 }
  t_ok = { [1] = 123 }

Invece, se nel costruttore omettiamo le chiavi otteniamo una tabella che svolgerà il ruolo di array con indici interi impliciti che cominceranno da 1, contrariamente alla maggior parte dei linguaggi dove l'indice comincia sempre da 0. Ecco un esempio:
  t = { 123, 456, 789 }
  print(t[1] + t[2] + t[3]) --> stampa 1368

Non è tutto. L'efficacia sintattica del costruttore è completata dalla dot notation comune in molti linguaggi a oggetti e valida solamente per le chiavi di tipo stringa: il campo di una chiave di tipo stringa si accede scrivendone la chiave dopo il nome del riferimento della tabella separato dal carattere ".":
  t = { chiave = "123" }
  assert(t.chiave == t["chiave"])

Prestate attenzione perché ci sono casi in cui all'inizio si può male interpretare il risultato del costruttore della tabella se unito alla dot notation:
  chiave = "ok"
  t = { ok = "123"}
  assert( t.ok == t[chiave] ) -- ok
  
  -- attenzione!
  k = "ok"
  print( t.k ) --> stampa nil: "k" è una chiave non definita in t
  print( t[k]) --> stampa "123" : vale infatti t[k] == t["ok"] == t.ok
  --> t.k diverso da t[k] !!!

Non confondete il nome di variabile con il nome del campo in dot notation!

Riassumendo, indicizzare una tabella con una variabile restituisce il valore associato alla chiave uguale al valore della variabile, mentre indicizzare in dot notation con il nome uguale a quello della variabile restituisce il valore associato alla chiave corrispondente alla stringa del nome.

Con la funzione assert() di Lua si può esprimere l'equivalenza logica tra due espressioni. Essa ritorna l'argomento se questo è vero oppure se non è nil, altrimenti ritorna un errore descritto eventualmente da un secondo argomento.

Questo perché in Lua un'espressione è vera se essa vale 'true' oppure se non vale nil.

Esercizi (e domande)

Scrivere un programma che memorizzi in una tabella i primi 10 numeri primi.

Utilizzando la 'dot notation' è possibile utilizzare caratteri spazio nel nome della chiave delle tabelle?

Scrivere un programma che stampi il valore associato alle chiavi 'paese' e 'codice', e il numero medio di comuni per regione, per la seguente tabella Lua. Stampare inoltre il numero di abitanti della capitale.
  t = {
    paese = "Italia",
    lingua = "italiano",
    codice = "IT",
    regioni = 20,
    provincie = 110,
    comuni = 8047,
    capitale = {"Roma", "RM", abitanti = 2753000},
}

Riassunto della puntata

Abbiamo introdotto la tabella di Lua, l'unico tipo strutturato predefinito di Lua e il suo motore di funzionalità principale. Fate gli esercizi proposti e qualche prova in modalità interattiva per fissare i concetti di indicizzazione, costruttore e dot notation di una tabella.

Con questa puntata si conclude la fase introduttiva al linguaggio. La luna comincia a splendere ed è ormai piena.

3 commenti: