Vai al contenuto

Lua/Istruzioni

Wikibooks, manuali e libri di testo liberi.
< Lua
Indice del libro

Le istruzioni (statement) sono pezzi di codice che possono essere eseguiti e che contengono un comando e delle espressioni da usare con esso. Alcune istruzioni conterranno anche codice al loro interno che può, ad esempio, essere eseguito in determinate condizioni. Diversamente dalle espressioni, possono essere inserite direttamente nel codice e verranno eseguite. Lua ha poche istruzioni, ma queste istruzioni, combinate con altre istruzioni e con espressioni complesse, danno una buona dose di controllo e flessibilità all'utente.

Assegnamento

[modifica | modifica sorgente]

I programmatori hanno spesso bisogno di poter memorizzare valori in memoria per poterli usare in seguito. Questo viene fatto usando le variabili. Le variabili sono riferimenti a un valore che è memorizzato nella memoria del computer. Possono essere usate per accedere a un numero in un secondo momento, dopo averlo memorizzato nella memoria. L'assegnamento è l'istruzione che viene usata per assegnare un valore a una variabile. Consiste nel nome della variabile in cui il valore dovrebbe essere memorizzato, seguito da un segno di uguale e dal valore che deve essere memorizzato nella variabile:

variabile = 43
print(variabile) --> 43

Come mostrato nel codice sopra, è possibile accedere al valore di una variabile inserendo il nome della variabile nel punto in cui si desidera che compaia quel valore.

L'operatore di assegnamento

[modifica | modifica sorgente]

In Lua, come nella maggior parte dei linguaggi di programmazione, il segno di uguale (=) funge da operatore di assegnamento diadico, assegnando il valore dell'espressione dell'operando di destra alla variabile denominata dall'operando di sinistra.

Assegnamento delle variabili

[modifica | modifica sorgente]

Gli esempi seguenti mostrano l'uso del segno di uguale per l'assegnamento delle variabili:

frutto = "mela"   -- assegna una stringa a una variabile
count = 5         -- assegna un valore numerico a una variabile

Stringhe e valori numerici

[modifica | modifica sorgente]

Si noti che le stringhe letterali devono essere racchiuse tra virgolette per distinguerle dai nomi delle variabili:

mele = 5
preferito = "mele"   -- senza le virgolette, mele verrebbe interpretato come un nome di variabile

Si noti che i valori numerici non devono essere racchiusi tra virgolette e non possono essere interpretati erroneamente come nomi di variabili, perché i nomi delle variabili non possono iniziare con un numero:

mele = 6    -- non sono necessarie virgolette attorno a un parametro numerico
pere = "5"  -- le virgolette faranno sì che il valore venga considerato una stringa

Assegnazioni multiple

[modifica | modifica sorgente]

Lua supporta più assegnazioni:

mele, preferito = 5, "mele" -- assegna mele = 5, preferito = "mele"

Identificatori

[modifica | modifica sorgente]

Gli identificatori in Lua sono anche chiamati nomi (names). Possono essere un qualsiasi testo composto da lettere, cifre e underscore, e che non inizi con una cifra. Sono usati per nominare variabili e campi di tabella, che saranno trattati nel capitolo sulle tabelle.

Ecco alcuni nomi validi:

  • nome
  • ciao
  • _
  • _pomodori
  • me41
  • __
  • _questoEun_nome23valido

Ecco alcuni nomi non validi:

  • 2ciao: inizia con una cifra,
  • th$i: contiene un carattere che non è una lettera, una cifra o un underscore,
  • hel!o: contiene un carattere che non è una lettera, una cifra o un underscore,
  • 563testo: inizia con una cifra,
  • 82_qualcosa: inizia con una cifra.

Inoltre, le seguenti parole chiave sono riservate da Lua e non possono essere utilizzate come nomi: and, break, do, else, elseif, end, false, for, function, if, in, local, nil, not, or, repeat, return, then, true, until, while.

Quando si nomina una variabile o un campo di tabella, è necessario scegliere un nome valido. Deve quindi iniziare con una lettera o un trattino basso e contenere solo lettere, trattini bassi e cifre. Si noti che Lua è sensibile alle maiuscole e alle minuscole. Ciò significa che Ciao e ciao sono due nomi diversi.

L'ambito di applicazione di una variabile è la parte del codice del programma in cui quella variabile ha significato. Gli esempi di variabili che hai visto prima sono tutti esempi di variabili globali, variabili a cui si può accedere da qualsiasi punto del programma. Le variabili locali, invece, possono essere utilizzate solo dalla parte del programma in cui sono state definite e nelle parti del programma che si trovano all'interno di quella parte del programma. Sono create esattamente nello stesso modo delle variabili globali, ma devono essere precedute dalla parola chiave local.

L'istruzione do verrà utilizzata per descriverli. L'istruzione do è un'istruzione che non ha altro scopo se non quello di creare un nuovo blocco di codice e quindi un nuovo ambito. Termina con la parola chiave end:

local variabile = 13 -- Definisce una variabile locale a cui è possibile accedere da qualsiasi punto dello script poiché è stata definita nella parte principale.
do
	-- Questa istruzione crea un nuovo blocco e anche un nuovo ambito.
	variabile = variabile + 5 -- Aggiunge 5 alla variabile, che ora è uguale a 18.
	local variabile = 17 -- Crea una variabile con lo stesso nome della variabile precedente, ma questa è locale e relativa all'ambito creato dall'istruzione do.
	variabile = variabile - 1 -- Sottrae 1 dalla variabile locale, che ora è uguale a 16.
	print(variabile) --> 16
end
print(variabile) --> 18

Quando un ambito termina, tutte le variabili in esso contenute vengono eliminate. Le parti di codice possono utilizzare variabili definite nelle parti di codice in cui sono incluse, ma se le "sovrascrivono" definendo una variabile locale con lo stesso nome, tale variabile locale verrà utilizzata al posto di quella definita nell'altra parte di codice. Ecco perché la prima chiamata alla funzione print mostra 16 mentre il secondo, che è al di fuori dell'ambito creato dall'istruzione do, mostra 18.

In pratica, dovrebbero essere utilizzate solo variabili locali perché possono essere definite e sono accessibili più velocemente delle variabili globali, poiché sono memorizzate in registri anziché nell'ambiente della funzione corrente, come le variabili globali. I registri sono aree che Lua utilizza per memorizzare variabili locali per accedervi rapidamente e possono contenere solitamente fino a 200 variabili locali. Anche il processore, un componente importante di tutti i computer, ha registri, ma questi non sono correlati ai registri di Lua. Ogni funzione (incluso il thread principale, il nucleo del programma, che è anche una funzione) ha il suo ambiente, che è una tabella che utilizza indici per i nomi delle variabili e memorizza i valori di queste variabili nei valori che corrispondono a questi indici.

Forme di assegnamento

[modifica | modifica sorgente]

Assegnamento aumentato

[modifica | modifica sorgente]

L'assegnamento aumentato (augmented assignment), detto anche assegnamento composto, è un tipo di assegnamento che assegna a una variabile un valore relativo al suo valore precedente, ad esempio incrementando il valore corrente. Un equivalente del codice a += 8, che incrementa il valore di a di 8 – che troviamo in linguaggi come C, JavaScript, Ruby, Python – non esiste in Lua, il che significa che è necessario scrivere a = a + 8.

Assegnamento concatenato

[modifica | modifica sorgente]

L'assegnamento concatenato (hained assignment) è un tipo di assegnamento che assegna un singolo valore a molte variabili. In C e Python il codice a = b = c = d = 0, ad esempio, imposterebbe i valori di a, b, c e d a 0. In Lua, questo codice genererà un errore, quindi è necessario scrivere l'esempio precedente in questo modo:

d = 0
c = d -- oppure c = 0
b = c -- oppure b = 0
a = b -- oppure a = 0

Assegnamento parallelo

[modifica | modifica sorgente]

L'assegnamento parallelo (parallel assignment), chiamato anche assegnamento simultaneo e assegnamento multiplo, è un tipo di assegnamento che assegna simultaneamente valori diversi (ma possono anche essere lo stesso valore) a variabili diverse. A differenza dell'assegnamento concatenato e dell'assegnamento aumentato, l'assegnamento parallelo è disponibile in Lua.

L'esempio nella sezione precedente può essere così riscritto per utilizzare l'assegnamento parallelo:

a, b, c, d = 0, 0, 0, 0

Se si specificano più variabili che valori, ad alcune variabili non verrà assegnato alcun valore. Se si specificano più valori che variabili, i valori extra verranno ignorati. Più tecnicamente, l'elenco dei valori viene adattato alla lunghezza dell'elenco delle variabili prima che avvenga l'assegnamento, il che significa che i valori in eccesso vengono rimossi e che i valori nil extra vengono aggiunti alla fine per fargli avere la stessa lunghezza dell'elenco delle variabili. Se una chiamata di funzione è presente alla fine dell'elenco dei valori, i valori che restituisce verranno aggiunti alla fine di tale elenco, a meno che la chiamata di funzione non venga inserita tra parentesi.

Inoltre, a differenza della maggior parte dei linguaggi di programmazione, Lua consente di riassegnare i valori alle variabili tramite permutazione. Ad esempio:

prima_variabile, seconda_variabile  =  54, 87 
prima_variabile, seconda_variabile  =  seconda_variabile, prima_variabile 
print(prima_variabile, seconda_variabile)  --> 87 54

Questo funziona perché l'istruzione di assegnamento valuta tutte le variabili e i valori prima di assegnare qualsiasi cosa. Le assegnazioni vengono eseguite come se fossero realmente simultanee, il che significa che è possibile assegnare contemporaneamente un valore a una variabile e a un campo di una tabella indicizzato con il valore di quella variabile prima che le venga assegnato un nuovo valore. In altre parole, il seguente codice imposterà dictionary[2], e non dictionary[1], a 12:

dictionary = {}
index = 2
index, dictionary[index] = index - 1, 12

Istruzione condizionale

[modifica | modifica sorgente]

Le istruzioni condizionali sono istruzioni che controllano se un'espressione è vera e, se lo è, eseguono un certo pezzo di codice. Se l'espressione non è vera, saltano semplicemente quel pezzo di codice e il programma continua. In Lua l'unica istruzione condizionale usa l'istruzione if. False e nil sono entrambi considerati falsi, mentre tutto il resto è considerato vero.

local number = 6

if number < 10 then
	print("Il numero " .. number .. " è minore di dieci.")
end

-- Qui può essere presente altro codice, che verrà eseguito indipendentemente dal fatto che il codice nell'istruzione condizionale sia stato eseguito o meno.

Nel codice sopra, alla variabile number viene assegnato il numero 6 con un'istruzione di assegnamento. Quindi, un'istruzione condizionale controlla se il valore memorizzato nella variabile number è inferiore a dieci, che è il nostro caso. Se lo è, stampa "Il numero 6 è minore di dieci.".

È anche possibile eseguire un determinato pezzo di codice solo se l'espressione non è vera, utilizzando la parola chiave else, e concatenare istruzioni condizionali con la parola chiave elseif:

local number = 15

if number < 10 then
	print("Il numero è minore di dieci.")
elseif number < 100 then
	print("Il numero è maggiore o uguale a dieci, ma minore di cento.")
elseif number ~= 1000 and number < 3000 then
	print("Il numero è maggiore o uguale a cento, minore di tremila e non è esattamente mille.")
else
	print("Il numero è 1000 o maggiore di 2999.")
end

Si noti che il blocco else deve essere sempre l'ultimo. Non può esserci un blocco elseif dopo il blocco else. I blocchi elseif sono significativi solo se nessuno dei blocchi che li hanno preceduti è stato eseguito.

Gli operatori utilizzati per confrontare due valori, alcuni dei quali sono utilizzati nel codice sopra, sono chiamati operatori relazionali. Se la relazione è vera, restituiscono il valore booleano true. Altrimenti, restituiscono il valore booleano false.

uguale a non uguale a maggiore di meno di maggiore o uguale a minore o uguale a
Notazione matematica = > <
Operatore Lua == ~= > < >= <=

Il codice sopra riportato dimostra come la parola chiave and può essere utilizzata per combinare molte espressioni booleane in un'espressione condizionale.

Spesso, i programmatori hanno la necessità di eseguire un determinato blocco di codice o un blocco di codice simile più volte, oppure di eseguire un determinato codice un numero di volte che può dipendere dall'input dell'utente. Un ciclo (loop) è una sequenza di istruzioni che viene definita una sola volta, ma che può essere eseguita più volte consecutivamente.

Cicli controllati da condizioni

[modifica | modifica sorgente]

I cicli controllati da condizioni (condition-controlled loops) sono molto simili alle istruzioni condizionali, ma invece di eseguire il codice se la condizione è vera e saltarla altrimenti, continueranno a eseguirla finché la condizione è vera o finché la condizione non è falsa. Lua ha due istruzioni per i cicli controllati da condizioni: il ciclo while e il ciclo repeat. Tali cicli eseguiranno il codice, quindi controlleranno se la condizione è vera. Se è vera, eseguiranno di nuovo il codice e lo ripeteranno finché la condizione non sarà falsa. Quando la condizione è falsa, smettono di ripetere il codice e il flusso del programma continua. Ogni esecuzione del codice è chiamata iterazione. La differenza tra while e repeat è che i cicli repeat controlleranno la condizione alla fine del ciclo mentre i cicli while la controlleranno all'inizio del ciclo. Questo fa la differenza solo per la prima iterazione: i cicli repeat eseguiranno sempre il codice almeno una volta, anche se la condizione è falsa la prima volta che il codice viene eseguito. Questo non è il caso dei cicli while, che eseguiranno il codice solo la prima volta se la condizione è effettivamente vera.

local number = 0

while number < 10 do
	print(number)
	number = number + 1 -- Aumenta il valore del numero di uno. 
end

Il codice sopra stamperà 0, poi 1, poi 2, poi 3 e così via, fino a 9. Dopo la decima iterazione, il numero non sarà più inferiore a dieci e quindi il ciclo smetterà di essere eseguito. A volte, i cicli saranno pensati per essere eseguiti all'infinito, nel qual caso sono chiamati cicli infiniti. I renderer, processi software che disegnano elementi sullo schermo, spesso eseguono un ciclo continuo per ridisegnare lo schermo e aggiornare l'immagine mostrata all'utente. Questo è il caso dei videogiochi, dove la vista del gioco deve essere aggiornata costantemente per assicurarsi che ciò che l'utente vede sia sempre aggiornato. Tuttavia, i casi in cui i cicli devono essere eseguiti all'infinito sono rari e tali cicli saranno spesso il risultato di errori. I cicli infiniti possono richiedere molte risorse del computer, quindi è importante assicurarsi che i cicli terminino sempre anche se l'utente riceve un input inaspettato.

local number = 0

repeat
	print(number)
	number = number + 1
until number >= 10

Il codice sopra farà esattamente la stessa cosa del codice che ha usato un ciclo while. La differenza principale è che, a differenza dei cicli while, in cui la condizione è inserita tra la parola chiave while e la parola chiave do, la condizione è inserita alla fine del ciclo, dopo la parola chiave until. Il ciclo repeat è l'unica istruzione in Lua che crea un blocco e che non è chiusa dalla parola chiave end.

Cicli controllati dal contatore

[modifica | modifica sorgente]

Incrementare una variabile significa aumentarne il valore per step, in particolare per step di uno. I due cicli nella sezione precedente hanno incrementato il numero della variabile e l'hanno usata per eseguire il codice un certo numero di volte. Questo tipo di cicli è così comune che la maggior parte dei linguaggi, incluso Lua, ha una struttura di controllo incorporata per esso. Questa struttura di controllo è chiamata count-controlled loop e, in Lua e nella maggior parte dei linguaggi, è definita dall'istruzione for. La variabile usata in tali loop è chiamata loop counter.

for number = 0, 9, 1 do
	print(number)
end

Il codice sopra fa esattamente la stessa cosa dei due cicli presentati nella sezione precedente, ma la variabile number è accessibile solo dall'interno del ciclo perché è locale a esso. Il primo numero che segue il nome della variabile e il simbolo di uguaglianza è l'inizializzazione. È il valore a cui viene inizializzato il contatore del ciclo. Il secondo numero è il numero a cui si ferma il ciclo. Incrementerà la variabile e ripeterà il codice finché la variabile non raggiunge questo numero. Infine, il terzo numero è l'incremento: è il valore di cui viene aumentato il contatore del ciclo a ogni iterazione. Se l'incremento non viene specificato, verrà assunto come 1 da Lua. Il codice sottostante mostrerà 1, 1.1, 1.2, 1.3, 1.4 e 1.5.

for n = 1, 2, 0.1 do
	print(n)
	if n >= 1.5 then
		break -- Termina immediatamente il ciclo e non lo ripete.
	end
end

Il motivo per cui il codice sopra non arriva fino a 2 e solo fino a 1,5 è dovuto all'istruzione break, che termina immediatamente il ciclo. Questa istruzione può essere utilizzata con qualsiasi ciclo, inclusi i cicli while e repeat. Nota che qui è stato utilizzato l'operatore >=, anche se teoricamente sarebbe andato bene anche l'operatore ==. Ciò è dovuto a errori di precisione decimale. Lua rappresenta i numeri con il formato a virgola mobile (floating-point format) a doppia precisione, che memorizza i numeri nella memoria come un'approssimazione del loro valore effettivo. In alcuni casi, l'approssimazione corrisponderà esattamente al numero, ma in altri casi sarà, appunto, solo un'approssimazione. Di solito, queste approssimazioni saranno abbastanza vicine al numero da non fare differenza, ma questo sistema può causare errori quando si utilizza l'operatore di uguaglianza. Ecco perché è generalmente più sicuro, quando si lavora con numeri decimali, evitare di utilizzare l'operatore di uguaglianza. In questo caso specifico, il codice non avrebbe funzionato se fosse stato utilizzato l'operatore di uguaglianza[1] (avrebbe continuato a salire fino a 1,9), ma funziona con l'operatore >=.

Un blocco è un elenco di istruzioni eseguite in sequenza. Queste istruzioni possono includere istruzioni vuote, che non contengono alcuna istruzione. Le istruzioni vuote possono essere utilizzate per iniziare un blocco con un punto e virgola o scrivere due punti e virgola in sequenza.

Le chiamate e le assegnazioni di funzione possono iniziare con una parentesi, il che può portare a un'ambiguità. Questo frammento ne è un esempio:

a = b + c
(print or io.write)('done')

Questo codice può essere interpretato in due modi:

a = b + c(print or io.write)('done')
a = b + c; (print or io.write)('done')

L'attuale parser vede sempre tali costruzioni nel primo modo, interpretando la parentesi di apertura come l'inizio degli argomenti di una chiamata. Per evitare questa ambiguità, è una buona norma far precedere sempre da un punto e virgola le istruzioni che iniziano con una parentesi:

;(print or io.write)('done')

L'unità di compilazione di Lua è chiamata chunk. Un chunk può essere memorizzato in un file o in una stringa all'interno del programma host. Per eseguire un chunk, Lua prima precompila il chunk in istruzioni per una macchina virtuale, quindi esegue il codice compilato con un interprete per la macchina virtuale. I chunk possono anche essere precompilati in forma binaria (bytecode) utilizzando luac, il programma di compilazione fornito con Lua, o la funzione string.dump, che restituisce una stringa contenente una rappresentazione binaria della funzione che gli viene fornita.

La funzione load può essere usata per caricare un chunk. Se il primo parametro dato alla funzione load è una stringa, il chunk è quella stringa. In questo caso, la stringa può essere codice Lua o bytecode Lua. Se il primo parametro è una funzione, load chiamerà quella funzione ripetutamente per ottenere i pezzi del chunk, poiché ogni pezzo è una stringa che verrà concatenata con le stringhe precedenti. Si considera che il chunk sia completo quando non viene restituito nulla o la stringa vuota.

La funzione load restituirà il chunk compilato come funzione se non ci sono errori sintattici. Altrimenti, restituirà nil e il messaggio di errore.

Il secondo parametro della funzione load è usato per impostare la sorgente del chunk. Tutti i chunk mantengono una copia della loro sorgente al loro interno, per poter fornire messaggi di errore e informazioni di debug appropriati. Per impostazione predefinita, quella copia della loro sorgente sarà il codice dato a load (se è stato dato un codice; se è stata data una funzione, sarà =(load)). Questo parametro può essere usato per cambiarlo. Ciò è utile soprattutto quando si compila un codice per impedire alle persone di recuperare la sorgente originale. È quindi necessario rimuovere la sorgente inclusa con la rappresentazione binaria perché codice originale può essere ottenuto lì.

Il terzo parametro della funzione load può essere utilizzato per impostare l'ambiente della funzione generata e il quarto parametro controlla se il chunk può essere testo o binario. Può essere la stringa "b" (solo chunk binari), "t" (solo chunk di testo) o "bt" (sia binario che di testo). Il valore predefinito è "bt".

Esiste anche una >funzione loadfile che funziona esattamente come load, ma invece ottiene il codice da un file. Il primo parametro è il nome del file da cui ottenere il codice. Non c'è alcun parametro per modificare la sorgente memorizzata nella rappresentazione binaria, e il terzo e il quarto parametro della funzione load corrispondono al secondo e al terzo parametro di questa funzione. La funzione loadfile può anche essere utilizzata per caricare codice dall'input standard, cosa che verrà fatta se non viene fornito alcun nome di file.

La funzione dofile è simile alla funzione loadfile, ma invece di caricare il codice in un file come funzione, esegue immediatamente il codice contenuto in un file di codice sorgente come un chunk Lua. Il suo unico parametro è usato per specificare il nome del file di cui dovrebbe eseguire il contenuto; se non viene fornito alcun argomento, eseguirà il contenuto dell'input standard. Se il chunk restituisce valori, questi saranno forniti dalla chiamata alla funzione dofile. Poiché dofile non viene eseguito in modalità protetta, tutti gli errori nei chunk eseguiti tramite esso verranno propagati.

  1. http://codepad.org/kYHPSvqx