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:

  1. 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
    
  2. 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 Variadici

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


Modified text is an extract of the original Stack Overflow Documentation
Autorizzato sotto CC BY-SA 3.0
Non affiliato con Stack Overflow