Buscar..


Sintaxis

  • funcname = function (paramA, paramB, ...) body; return exprlist end - una función simple
  • función funcname (paramA, paramB, ...) body; return exprlist end - taquigrafía de arriba
  • local funcname = function (paramA, paramB, ...) body; return exprlist end - a lambda
  • funcname local; funcname = function (paramA, paramB, ...) body; return exprlist end - lambda que puede hacer llamadas recursivas
  • función local funcname (paramA, paramB, ...) body; return exprlist end - taquigrafía de arriba
  • funcname (paramA, paramB, ...) - llamar a una función
  • local var = var o "Predeterminado" - un parámetro predeterminado
  • devuelve nil, "mensajes de error" - forma estándar de abortar con un error

Observaciones

Las funciones generalmente se configuran con la function a(b,c) ... end y, rara vez, con el establecimiento de una variable en una función anónima ( a = function(a,b) ... end ). Lo contrario es cierto cuando se pasan las funciones como parámetros, la mayoría de las funciones anónimas se usan y las funciones normales no se usan con tanta frecuencia.

Definiendo una función

function add(a, b)
    return a + b
end
-- creates a function called add, which returns the sum of it's two arguments

Veamos la sintaxis. En primer lugar, vemos una palabra clave de function . Bueno, eso es bastante descriptivo. A continuación vemos el identificador add ; el nombre. Luego vemos los argumentos (a, b) pueden ser cualquier cosa, y son locales. Solo dentro del cuerpo de la función podemos acceder a ellos. Vamos a saltar al final, vemos ... bueno, ¡el end ! Y todo lo que está en medio es la función del cuerpo; El código que se ejecuta cuando se llama. La palabra clave return es lo que hace que la función realmente dé un resultado útil. Sin ella, la función no devuelve nada, lo que equivale a devolver nil. Por supuesto, esto puede ser útil para cosas que interactúan con IO, por ejemplo:

function printHello(name)
    print("Hello, " .. name .. "!");
end 

En esa función, no usamos la declaración de retorno.

Las funciones también pueden devolver valores de forma condicional, lo que significa que una función tiene la opción de no devolver nada (nulo) o un valor. Esto se demuestra en el siguiente ejemplo.

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

También es posible que una función devuelva múltiples valores separados por comas, como se muestra:

function doOperations(a, b)
    return a+b, a-b, a*b
end

added, subbed, multiplied = doOperations(4,2)

Las funciones también pueden ser declaradas locales.

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

También se pueden guardar en tablas:

tab = {function(a,b) return a+b end}
(tab[1])(1, 2) --> returns 3

Llamando a una función.

Las funciones solo son útiles si podemos llamarlas. Para llamar a una función se usa la siguiente sintaxis:

print("Hello, World!")

Estamos llamando a la función de print . Usando el argumento "Hello, World" . Como es obvio, esto imprimirá Hello, World al flujo de salida. El valor devuelto es accesible, al igual que cualquier otra variable sería.

local added = add(10, 50) -- 60

Las variables también se aceptan en los parámetros de una función.

local a = 10
local b = 60

local c = add(a, b)

print(c)

Las funciones que esperan una tabla o una cadena pueden invocarse con un azúcar sintáctico limpio: se pueden omitir los paréntesis que rodean la llamada.

print"Hello, world!"
for k, v in pairs{"Hello, world!"} do print(k, v) end

Funciones anonimas

Creando funciones anonimas

Las funciones anónimas son como las funciones normales de Lua, excepto que no tienen un nombre.

doThrice(function()
    print("Hello!")
end)

Como puede ver, la función no está asignada a ningún nombre como print o add . Para crear una función anónima, todo lo que tiene que hacer es omitir el nombre. Estas funciones también pueden tomar argumentos.

Entendiendo el azúcar sintáctico.

Es importante entender que el siguiente código

function double(x)
    return x * 2
end

En realidad es sólo una taquigrafía para

double = function(x)
    return x * 2
end

Sin embargo, la función anterior no es anónima, ya que la función se asigna directamente a una variable.

Las funciones son valores de primera clase.

Esto significa que una función es un valor con los mismos derechos que los valores convencionales como números y cadenas. Las funciones se pueden almacenar en variables, en tablas, se pueden pasar como argumentos y otras funciones pueden devolverlas.

Para demostrar esto, también crearemos una función "media":

half = function(x)
    return x / 2
end

Entonces, ahora tenemos dos variables, half y double , ambas contienen una función como valor. ¿Qué pasaría si quisiéramos crear una función que alimentaría el número 4 en dos funciones dadas y calcularíamos la suma de ambos resultados?

Queremos llamar a esta función como sumOfTwoFunctions(double, half, 4) . Esto alimentará la función double , la half función y el entero 4 en nuestra propia función.

function sumOfTwoFunctions(firstFunction, secondFunction, input)
    return firstFunction(input) + secondFunction(input)
end

La función sumOfTwoFunctions anterior muestra cómo se pueden pasar las funciones dentro de los argumentos y se puede acceder a ellos con otro nombre.

Parámetros por defecto

function sayHello(name)
    print("Hello, " .. name .. "!")
end

Esa función es una función simple, y funciona bien. Pero, ¿qué pasaría si acabamos de llamar 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 ?

Eso no es exactamente genial. Hay dos maneras de arreglar esto:

  1. Inmediatamente regresas de la función:

    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. Establece un parámetro por defecto .

    Para hacer esto, simplemente usa esta expresión simple

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

El name = name or "Jack" idioma name = name or "Jack" funciona porque or en los cortocircuitos de Lua. Si el elemento en el lado izquierdo de un or es algo distinto de nil o false , el lado derecho nunca se evalúa. Por otro lado, si se llama a sayHello sin parámetro, entonces el name será nil , por lo que la cadena "Jack" se asignará al name . (Tenga en cuenta que este idioma, por lo tanto, no funcionará si el false booleano es un valor razonable para el parámetro en cuestión).

Múltiples resultados

Las funciones en Lua pueden devolver múltiples resultados.

Por ejemplo:

function triple(x)
    return x, x, x
end

Al llamar a una función, para guardar estos valores, debe usar la siguiente sintaxis:

local a, b, c = triple(5)

Lo que resultará en a = b = c = 5 en este caso. También es posible ignorar los valores devueltos utilizando la variable desechable _ en el lugar deseado en una lista de variables:

local a, _, c = triple(5)

En este caso, el segundo valor devuelto será ignorado. También es posible ignorar los valores de retorno al no asignarlos a ninguna variable:

local a = triple(5)

A la variable a se le asignará el primer valor de retorno y los dos restantes se descartarán.

Cuando una función devuelve una cantidad variable de resultados, uno puede almacenarlos todos en una tabla, ejecutando la función dentro de ella:

local results = {triple(5)}

De esta manera, uno puede iterar sobre la tabla de results para ver qué función devolvió.

Nota

Esto puede ser una sorpresa en algunos casos, por ejemplo:

local t = {}
table.insert(t, string.gsub("  hi", "^%s*(.*)$", "%1")) --> bad argument #2 to 'insert' (number expected, got string)

Esto sucede porque string.gsub devuelve 2 valores: la cadena dada, con las ocurrencias del patrón reemplazado y el número total de coincidencias que ocurrieron.

Para resolver esto, use una variable intermedia o ponga () alrededor de la llamada, así:

table.insert(t, (string.gsub("  hi", "^%s*(.*)$", "%1"))) --> works. t = {"hi"}

Esto toma solo el primer resultado de la llamada, e ignora el resto.

Número variable de argumentos

Argumentos Variados

Argumentos con nombre

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

Comprobando tipos de argumentos

Algunas funciones solo funcionan en un cierto tipo de argumento:

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

Esto tiene varias implicaciones:

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"

ahora podemos llamar a la función con cualquier parámetro que queramos, y no bloqueará el programa.

-- 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

Entonces, ¿qué pasa si tenemos una función que hace algo con una instancia de una clase específica? Esto es difícil, porque las clases y los objetos suelen ser tablas, por lo que la función type devolverá '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

Solución: comparar los metatables.

-- 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

Cierres

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

ejemplo de uso típico

function new_adder(number)
    return function(input)
        return input + number
    end
end
add_3 = new_adder(3)
print(add_3(2)) --> prints 5

ejemplo de uso más avanzado

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
Licenciado bajo CC BY-SA 3.0
No afiliado a Stack Overflow