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