Variabili e Tipi di Dati
Lua e un linguaggio a tipizzazione dinamica: le variabili non hanno un tipo fisso, ma il tipo e associato al valore che contengono in un dato momento. Nonostante questa flessibilita, Lua dispone di un sistema di tipi preciso e ben definito. In questo articolo esploreremo tutti gli 8 tipi di dati di Lua, la gestione delle variabili e le regole di coercizione.
Gli 8 Tipi di Dati di Lua
Lua ha esattamente 8 tipi fondamentali. Puoi verificare il tipo di qualsiasi valore con la funzione type():
print(type(nil)) --> nil
print(type(true)) --> boolean
print(type(42)) --> number
print(type("ciao")) --> string
print(type(print)) --> function
print(type({})) --> table
print(type(io.stdin)) --> userdata
print(type(coroutine.create(function() end))) --> thread
La funzione type() restituisce sempre una stringa con il nome del tipo.
1. nil
nil rappresenta l’assenza di valore. E l’unico valore del tipo nil e ha un ruolo fondamentale in Lua:
local x -- x vale nil (non inizializzata)
print(x) --> nil
print(type(x)) --> nil
-- nil e usato per "eliminare" variabili e campi di tabelle
local t = {a = 1, b = 2}
t.b = nil -- rimuove il campo "b" dalla tabella
In un contesto booleano, nil e considerato falso.
2. boolean
Il tipo boolean ha solo due valori: true e false:
local attivo = true
local errore = false
print(type(attivo)) --> boolean
-- In Lua, solo nil e false sono "falsy"
-- Tutto il resto e "truthy", incluso 0 e la stringa vuota!
if 0 then
print("0 e truthy in Lua!") --> viene stampato!
end
if "" then
print("Stringa vuota e truthy!") --> viene stampato!
end
Questa e una differenza importante rispetto a linguaggi come Python o JavaScript, dove 0 e "" sono considerati falsi.
3. number
In Lua 5.4, i numeri si dividono in integer (interi a 64 bit) e float (virgola mobile a doppia precisione):
local intero = 42
local decimale = 3.14
local esponenziale = 1.5e10
local esadecimale = 0xFF -- 255
print(type(intero)) --> number
print(type(decimale)) --> number
-- Verifica se un numero e intero o float
print(math.type(42)) --> integer
print(math.type(42.0)) --> float
-- Conversione tra integer e float
print(42 + 0.0) --> 42.0 (diventa float)
print(math.tointeger(42.0)) --> 42 (diventa integer)
-- Divisione intera vs divisione float
print(7 / 2) --> 3.5 (divisione float)
print(7 // 2) --> 3 (divisione intera)
4. string
Le stringhe in Lua sono sequenze immutabili di byte. Possono essere definite con apici singoli, doppi o doppie parentesi quadre:
local s1 = "Ciao Mondo"
local s2 = 'Anche gli apici singoli funzionano'
local s3 = [[
Stringa su
piu righe
]]
-- Le stringhe sono immutabili
local nome = "Lua"
-- nome[1] = "l" -- ERRORE: non puoi modificare una stringa
-- Lunghezza di una stringa
print(#nome) --> 3
-- Concatenazione
local saluto = "Ciao" .. " " .. nome
print(saluto) --> Ciao Lua
-- Sequenze di escape comuni
local speciali = "Tab:\tNuova riga:\nBackslash:\\"
print(speciali)
5. function
In Lua le funzioni sono valori di prima classe: possono essere assegnate a variabili, passate come argomenti e restituite da altre funzioni:
-- Funzione assegnata a una variabile
local somma = function(a, b)
return a + b
end
print(type(somma)) --> function
print(somma(3, 4)) --> 7
-- Funzione come argomento
local function applica(fn, x, y)
return fn(x, y)
end
print(applica(somma, 10, 20)) --> 30
-- La stessa print e una funzione
print(type(print)) --> function
6. table
La tabella e l’unica struttura dati composita in Lua. Serve come array, dizionario, oggetto, modulo e molto altro:
-- Tabella come array (indici numerici, partono da 1)
local frutti = {"mela", "pera", "banana"}
print(frutti[1]) --> mela
print(#frutti) --> 3
-- Tabella come dizionario (coppie chiave-valore)
local persona = {
nome = "Marco",
eta = 30,
citta = "Roma"
}
print(persona.nome) --> Marco
print(persona["eta"]) --> 30
-- Tabelle miste
local mista = {
"primo", -- indice 1
chiave = "valore", -- chiave stringa
[10] = "decimo", -- indice numerico esplicito
}
7. userdata
Il tipo userdata rappresenta dati C arbitrari memorizzati in variabili Lua. E utilizzato principalmente per l’interfacciamento con librerie C:
-- Esempio: i file handle sono userdata
local file = io.open("test.txt", "r")
if file then
print(type(file)) --> userdata
file:close()
end
I programmatori Lua comuni raramente creano userdata direttamente; vengono forniti dalle librerie C.
8. thread
Il tipo thread rappresenta una coroutine, un flusso di esecuzione indipendente:
local co = coroutine.create(function()
print("Dentro la coroutine")
coroutine.yield()
print("Ripresa la coroutine")
end)
print(type(co)) --> thread
coroutine.resume(co) --> Dentro la coroutine
coroutine.resume(co) --> Ripresa la coroutine
Variabili Locali e Globali
In Lua, le variabili sono globali per default. Si usa la parola chiave local per dichiarare variabili locali:
-- Variabile globale (SCONSIGLIATA nella maggior parte dei casi)
contatore = 0
-- Variabile locale (CONSIGLIATA)
local risultato = 42
-- Le variabili locali sono limitate al blocco in cui sono dichiarate
do
local temp = 100
print(temp) --> 100
end
-- print(temp) --> nil (temp non esiste piu)
E una best practice usare sempre local per le variabili. Le variabili globali inquinano il namespace, sono piu lente da accedere e rendono il codice piu difficile da mantenere:
-- SBAGLIATO: troppe variabili globali
nome = "Mario"
cognome = "Rossi"
eta = 25
-- CORRETTO: usa sempre local
local nome = "Mario"
local cognome = "Rossi"
local eta = 25
Tipizzazione Dinamica
In Lua il tipo e associato al valore, non alla variabile. La stessa variabile puo contenere valori di tipi diversi in momenti diversi:
local x = 10 -- x contiene un number
print(type(x)) --> number
x = "adesso sono una stringa"
print(type(x)) --> string
x = true
print(type(x)) --> boolean
x = {1, 2, 3}
print(type(x)) --> table
x = nil
print(type(x)) --> nil
Coercizione dei Tipi
Lua effettua automaticamente la coercizione (conversione implicita) tra stringhe e numeri in alcuni contesti:
-- Stringa convertita in numero nelle operazioni aritmetiche
print("10" + 5) --> 15
print("3.14" * 2) --> 6.28
-- Numero convertito in stringa nella concatenazione
print(42 .. " gatti") --> 42 gatti
print(type(42 .. "")) --> string
-- Conversione esplicita (consigliata per chiarezza)
local s = tostring(42) -- "42"
local n = tonumber("3.14") -- 3.14
-- Attenzione: la conversione puo fallire
local risultato = tonumber("abc")
print(risultato) --> nil (conversione fallita)
E buona pratica preferire la conversione esplicita con tonumber() e tostring() invece di affidarsi alla coercizione automatica.
Assegnamento Multiplo
Lua supporta l’assegnamento multiplo, permettendo di assegnare piu valori a piu variabili in una singola istruzione:
-- Assegnamento multiplo
local x, y, z = 10, 20, 30
print(x, y, z) --> 10 20 30
-- Se ci sono piu variabili che valori, le extra valgono nil
local a, b, c = 1, 2
print(a, b, c) --> 1 2 nil
-- Se ci sono piu valori che variabili, i valori extra sono scartati
local p, q = 10, 20, 30
print(p, q) --> 10 20
-- Scambio di variabili in una riga (idiomatico in Lua)
local primo, secondo = "A", "B"
primo, secondo = secondo, primo
print(primo, secondo) --> B A
L’assegnamento multiplo e particolarmente utile con le funzioni che restituiscono piu valori:
-- Le funzioni possono restituire piu valori
local function dividi(a, b)
return a // b, a % b -- quoziente e resto
end
local quoziente, resto = dividi(17, 5)
print("17 / 5 = " .. quoziente .. " con resto " .. resto)
--> 17 / 5 = 3 con resto 2
Conclusione
Il sistema di tipi di Lua e semplice ma completo. Con soli 8 tipi fondamentali, il linguaggio offre tutta la flessibilita necessaria per costruire programmi complessi. La regola d’oro e usare sempre local per le variabili, preferire le conversioni esplicite alla coercizione automatica e sfruttare l’assegnamento multiplo per scrivere codice piu pulito e idiomatico.