Ricerca…
Sintassi
- funcname = function (paramA, paramB, ...) corpo; return exprlist end - una funzione semplice
- function functionname (paramA, paramB, ...) corpo; return exprlist end - stenografia per sopra
- nome di funzione locale = funzione (paramA, paramB, ...); return exprlist end - a lambda
- funcname locale; funcname = function (paramA, paramB, ...) corpo; return exprlist end - lambda che può effettuare chiamate ricorsive
- corpo della funzione funzionale locale (paramA, paramB, ...); return exprlist end - stenografia per sopra
- funcname (paramA, paramB, ...) - chiama una funzione
- local var = var o "Default" - un parametro predefinito
- return nil, "messaggi di errore" - metodo standard per annullare un errore
Osservazioni
Le funzioni sono generalmente impostate con la function a(b,c) ... end
e raramente con l'impostazione di una variabile su una funzione anonima ( a = function(a,b) ... end
). È vero il contrario quando si passano le funzioni come parametri, si utilizzano principalmente le funzioni anonime e le funzioni normali non vengono utilizzate più spesso.
Definire una funzione
function add(a, b)
return a + b
end
-- creates a function called add, which returns the sum of it's two arguments
Diamo un'occhiata alla sintassi. Innanzitutto, vediamo una parola chiave function
. Bene, è abbastanza descrittivo. Successivamente vediamo l'identificatore di add
; il nome. Vediamo quindi gli argomenti (a, b)
questi possono essere qualsiasi cosa, e sono locali. Solo all'interno del corpo della funzione possiamo accedervi. Saltiamo fino alla fine, vediamo ... beh, la end
! E tutto ciò che c'è in mezzo è il corpo della funzione; il codice che viene eseguito quando viene chiamato. La parola chiave return
è ciò che rende la funzione effettivamente utile. Senza di esso, la funzione non restituisce nulla, che equivale a restituire nil. Questo può naturalmente essere utile per cose che interagiscono con IO, ad esempio:
function printHello(name)
print("Hello, " .. name .. "!");
end
In quella funzione, non abbiamo usato la dichiarazione di ritorno.
Le funzioni possono anche restituire i valori in modo condizionale, ovvero una funzione ha la possibilità di restituire nulla (nil) o un valore. Questo è dimostrato nel seguente esempio.
function add(a, b)
if (a + b <= 100) then
return a + b -- Returns a value
else
print("This function doesn't return values over 100!") -- Returns nil
end
end
È anche possibile che una funzione restituisca più valori separati da virgole, come mostrato:
function doOperations(a, b)
return a+b, a-b, a*b
end
added, subbed, multiplied = doOperations(4,2)
Le funzioni possono anche essere dichiarate locali
do
local function add(a, b) return a+b end
print(add(1,2)) --> prints 3
end
print(add(2, 2)) --> exits with error, because 'add' is not defined here
Possono essere salvati anche nelle tabelle:
tab = {function(a,b) return a+b end}
(tab[1])(1, 2) --> returns 3
Chiamare una funzione.
Le funzioni sono utili solo se possiamo chiamarle. Per chiamare una funzione viene utilizzata la seguente sintassi:
print("Hello, World!")
Stiamo chiamando la funzione di print
. Utilizzando l'argomento "Hello, World"
. Come è ovvio, questo stamperà Hello, World
sul flusso di output. Il valore restituito è accessibile, proprio come qualsiasi altra variabile.
local added = add(10, 50) -- 60
Le variabili sono anche accettate nei parametri di una funzione.
local a = 10
local b = 60
local c = add(a, b)
print(c)
Le funzioni che prevedono una tabella o una stringa possono essere chiamate con uno zucchero sintattico preciso: le parentesi che circondano la chiamata possono essere omesse.
print"Hello, world!"
for k, v in pairs{"Hello, world!"} do print(k, v) end
Funzioni anonime
Creazione di funzioni anonime
Le funzioni anonime sono come normali funzioni Lua, tranne che non hanno un nome.
doThrice(function()
print("Hello!")
end)
Come puoi vedere, la funzione non è assegnata a nessun nome come print
o add
. Per creare una funzione anonima, tutto ciò che devi fare è omettere il nome. Queste funzioni possono anche prendere argomenti.
Capire lo zucchero sintattico
È importante capire che il seguente codice
function double(x)
return x * 2
end
è in realtà solo una scorciatoia per
double = function(x)
return x * 2
end
Tuttavia, la funzione di cui sopra non è anonima in quanto la funzione è direttamente assegnata a una variabile!
Le funzioni sono valori di prima classe
Ciò significa che una funzione è un valore con gli stessi diritti dei valori convenzionali come numeri e stringhe. Le funzioni possono essere memorizzate in variabili, in tabelle, possono essere passate come argomenti e possono essere restituite da altre funzioni.
Per dimostrarlo, creeremo anche una funzione "metà":
half = function(x)
return x / 2
end
Quindi, ora abbiamo due variabili, half
e double
, entrambe contenenti una funzione come valore. E se volessimo creare una funzione che alimenterebbe il numero 4 in due funzioni date e calcolerebbe la somma di entrambi i risultati?
Vorremmo chiamare questa funzione come sumOfTwoFunctions(double, half, 4)
. Questo alimenterà la double
funzione, la half
funzione e l'intero 4
nella nostra funzione.
function sumOfTwoFunctions(firstFunction, secondFunction, input)
return firstFunction(input) + secondFunction(input)
end
La funzione sumOfTwoFunctions
precedente mostra come le funzioni possono essere passate all'interno degli argomenti e accessibili da un altro nome.
Parametri di default
function sayHello(name)
print("Hello, " .. name .. "!")
end
Quella funzione è una funzione semplice e funziona bene. Ma cosa succederebbe se chiamassimo semplicemente sayHello()
?
stdin:2: attempt to concatenate local 'name' (a nil value)
stack traceback:
stdin:2: in function 'sayHello'
stdin:1: in main chunk
[C]: in ?
Non è esattamente fantastico. Ci sono due modi per risolvere questo problema:
Si ritorna immediatamente dalla funzione:
function sayHello(name) if not (type(name) == "string") then return nil, "argument #1: expected string, got " .. type(name) end -- Bail out if there's no name. -- in lua it is a convention to return nil followed by an error message on error print("Hello, " .. name .. "!") -- Normal behavior if name exists. end
Hai impostato un parametro predefinito .
Per fare ciò, usa semplicemente questa semplice espressione
function sayHello(name)
name = name or "Jack" -- Jack is the default,
-- but if the parameter name is given,
-- name will be used instead
print("Hello, " .. name .. "!")
end
Il name = name or "Jack"
idioma name = name or "Jack"
funziona perché or
nei cortocircuiti di Lua. Se l'elemento sul lato sinistro di un or
è diverso da nil
o false
, il lato destro non viene mai valutato. D'altra parte, se sayHello
viene chiamato con nessun parametro, il name
sarà nil
, quindi la stringa "Jack"
verrà assegnata al name
. (Nota che questo idioma, quindi, non funzionerà se il valore booleano false
è un valore ragionevole per il parametro in questione.)
Più risultati
Le funzioni in Lua possono restituire più risultati.
Per esempio:
function triple(x)
return x, x, x
end
Quando si chiama una funzione, per salvare questi valori, è necessario utilizzare la seguente sintassi:
local a, b, c = triple(5)
Quale risultato in a = b = c = 5
in questo caso. È anche possibile ignorare i valori restituiti utilizzando la variabile throwaway _
nella posizione desiderata in un elenco di variabili:
local a, _, c = triple(5)
In questo caso, il secondo valore restituito verrà ignorato. È anche possibile ignorare i valori restituiti non assegnandoli a nessuna variabile:
local a = triple(5)
Alla variabile a
verrà assegnato il primo valore di ritorno e gli altri due verranno scartati.
Quando una funzione restituisce una quantità variabile di risultati, è possibile memorizzarli tutti in una tabella, eseguendo la funzione al suo interno:
local results = {triple(5)}
In questo modo, si può scorrere sulla tabella dei results
per vedere cosa ha restituito la funzione.
Nota
Questo può essere una sorpresa in alcuni casi, ad esempio:
local t = {}
table.insert(t, string.gsub(" hi", "^%s*(.*)$", "%1")) --> bad argument #2 to 'insert' (number expected, got string)
Ciò accade perché string.gsub
restituisce 2 valori: la stringa data, con le occorrenze del modello sostituito e il numero totale di corrispondenze verificatesi.
Per risolvere questo, utilizzare una variabile intermedia o put ()
attorno alla chiamata, in questo modo:
table.insert(t, (string.gsub(" hi", "^%s*(.*)$", "%1"))) --> works. t = {"hi"}
Questo afferra solo il primo risultato della chiamata e ignora il resto.
Numero variabile di argomenti
Argomenti nominati
local function A(name, age, hobby)
print(name .. "is " .. age .. " years old and likes " .. hobby)
end
A("john", "eating", 23) --> prints 'john is eating years old and likes 23'
-- oops, seems we got the order of the arguments wrong...
-- this happens a lot, specially with long functions that take a lot of arguments
-- and where the order doesn't follow any particular logic
local function B(tab)
print(tab.name .. "is " .. tab.age .. " years old and likes " .. tab.hobby)
end
local john = {name="john", hobby="golf", age="over 9000", comment="plays too much golf"}
B(john)
--> will print 'John is over 9000 years old and likes golf'
-- I also added a 'comment' argument just to show that excess arguments are ignored by the function
B({name = "tim"}) -- can also be written as
B{name = "tim"} -- to avoid cluttering the code
--> both will print 'tim is nil years old and likes nil'
-- remember to check for missing arguments and deal with them
function C(tab)
if not tab.age then return nil, "age not defined" end
tab.hobby = tab.hobby or "nothing"
-- print stuff
end
-- note that if we later decide to do a 'person' class
-- we just need to make sure that this class has the three fields
-- age, hobby and name, and it will be compatible with these functions
-- example:
local john = ClassPerson.new("John", 20, "golf") -- some sort of constructor
john.address = "some place" -- modify the object
john:do_something("information") -- call some function of the object
C(john) -- this works because objects are *usually* implemented as tables
Controllo dei tipi di argomenti
Alcune funzioni funzionano solo su un determinato tipo di argomento:
function foo(tab)
return tab.bar
end
--> returns nil if tab has no field bar, which is acceptable
--> returns 'attempt to index a number value' if tab is, for example, 3
--> which is unacceptable
function kungfoo(tab)
if type(tab) ~= "table" then
return nil, "take your useless " .. type(tab) .." somewhere else!"
end
return tab.bar
end
questo ha diverse implicazioni:
print(kungfoo(20)) --> prints 'nil, take your useless number somewhere else!'
if kungfoo(20) then print "good" else print "bad" end --> prints bad
foo = kungfoo(20) or "bar" --> sets foo to "bar"
ora possiamo chiamare la funzione con qualsiasi parametro che vogliamo, e non manderà in crash il programma.
-- if we actually WANT to abort execution on error, we can still do
result = assert(kungfoo({bar=20})) --> this will return 20
result = assert(kungfoo(20)) --> this will throw an error
Quindi, cosa succede se abbiamo una funzione che fa qualcosa con un'istanza di una classe specifica? Questo è difficile, perché le classi e gli oggetti sono in genere tabelle, quindi la funzione type
restituirà 'table'
.
local Class = {data="important"}
local meta = {__index=Class}
function Class.new()
return setmetatable({}, meta)
end
-- this is just a very basic implementation of an object class in lua
object = Class.new()
fake = {}
print(type(object)), print(type(fake)) --> prints 'table' twice
Soluzione: confronta i metabli
-- continuation of previous code snippet
Class.is_instance(tab)
return getmetatable(tab) == meta
end
Class.is_instance(object) --> returns true
Class.is_instance(fake) --> returns false
Class.is_instance(Class) --> returns false
Class.is_instance("a string") --> returns false, doesn't crash the program
Class.is_instance(nil) --> also returns false, doesn't crash either
chiusure
do
local tab = {1, 2, 3}
function closure()
for key, value in ipairs(tab) do
print(key, "I can still see you")
end
end
closure()
--> 1 I can still see you
--> 2 I can still see you
--> 3 I can still see you
end
print(tab) --> nil
-- tab is out of scope
closure()
--> 1 I can still see you
--> 2 I can still see you
--> 3 I can still see you
-- the function can still see tab
esempio di utilizzo tipico
function new_adder(number)
return function(input)
return input + number
end
end
add_3 = new_adder(3)
print(add_3(2)) --> prints 5
esempio di utilizzo più avanzato
function base64.newDecoder(str) -- Decoder factory
if #str ~= 64 then return nil, "string must be 64 characters long!" end
local tab = {}
local counter = 0
for c in str:gmatch"." do
tab[string.byte(c)] = counter
counter = counter + 1
end
return function(str)
local result = ""
for abcd in str:gmatch"..?.?.?" do
local a, b, c, d = string.byte(abcd,1,-1)
a, b, c, d = tab[a], tab[b] or 0, tab[c] or 0, tab[d] or 0
result = result .. (
string.char( ((a<<2)+(b>>4))%256 ) ..
string.char( ((b<<4)+(c>>2))%256 ) ..
string.char( ((c<<6)+d)%256 )
)
end
return result
end
end