Tutorial de Lua

Lua es un lenguaje de scripting desarrollado en Brasil a principios de los años 90. El código fuente de un programa redactado en Lua es traducido a bytecode por un intérprete de Lua, que también lo ejecuta. El intérprete en sí está escrito en el lenguaje C, lo cual otorga a los programas escritos en Lua un mayor rendimiento al ejecutarse. Además, la API de C permite embeber (es decir, insertar) código Lua en programas en C / C++. Lua es un lenguaje multiparadigma pensado para redactar código imperativo, funcional y orientado a objetos.

La característica que hace único a Lua es que puede embeberse (incrustarse) de forma muy sencilla en otros sistemas y lenguajes. Por este motivo, Lua se ha convertido en un importante lenguaje pegamento (glue language) y se usa en muchos motores de videojuego. También puede usarse para dirigir servidores web como Apache y nginx. A través de la interfaz de entrada común (o CGI, por sus siglas en inglés), Lua también es usado a menudo como lenguaje de programación para Internet de forma autónoma. Por si eso fuera poco, también es usado para programar apps móviles.

Tutorial para aprender Lua: primeros pasos

La manera más rápida y sencilla de aprender a programar en Lua es ejecutar código desde la interactiva plataforma demo de Lua. Allí podrás poner a prueba todos los ejemplos de código que mostramos en este artículo. Para ello, copia alguno de los ejemplos en el campo de texto y haz clic en “run” para ejecutarlo.

De este modo, puedes ahorrarte la instalación. Si, en cambio, quieres usar Lua en tu propio sistema, sigue leyendo y te explicamos cómo. De lo contrario, puedes saltar directamente al apartado “Aprender las bases de Lua”.

Preparar tu sistema para el tutorial de Lua

El intérprete de Lua está compuesto por un único archivo binario, que se abrirá al ejecutar la orden “lua” en la línea de comandos. El archivo se guarda entonces en el sistema y se le adjudica, si es necesario, una ruta. Los usuarios de Lua también tienen bibliotecas a su disposición que hacen posible la incrustación de código Lua en programas en C / C++. 

Si quieres instalar Lua en Mac o Linux, el gestor de paquetes Homebrew es una muy buena opción. Una vez hayas instalado Homebrew, utiliza simplemente el comando que aparece a continuación para instalar Lua: 

brew install lua

Para instalar Lua en un sistema Windows, en cambio, puedes usar el gestor de paquetes LuaDist.

Aprender Lua: cómo usar el intérprete de Lua de forma interactiva

Al igual que ocurre en muchos lenguajes de scripting, el intérprete de Lua también puede ejecutarse en modo interactivo. En este modo, el intérprete toma el código Lua de la línea de comandos y lo ejecuta línea a línea. Los valores generados como resultado se muestran directamente en la línea de comandos y el usuario puede entonces comprobar y modificar los valores de las variables. Por eso, este método resulta especialmente útil para crear prototipos rápidos. Para iniciar el intérprete de Lua en modo interactivo, introduce la siguiente orden en la línea de comandos:

# Iniciar el intérprete de Lua en modo interactivo
lua -i
Nota

Para desactivar de nuevo el modo interactivo, introduce la orden “os.exit()” o usa la combinación de teclas [Ctrl]+[C].

Tutorial de Lua: ejecutar código Lua con el intérprete

En lugar de ir pegando fragmentos de código en la línea de comandos, puedes ordenarle al intérprete de Lua que ejecute el archivo con el código fuente al completo. Para ello, primero hay que crear un archivo Lua y luego indicar su nombre al intérprete para que lo ejecute. El intérprete procesará el código fuente del archivo línea a línea y lo ejecutará.

# Ejecutar código Lua
lua <nombre del archivo>.lua
Nota

Los archivos de código fuente Lua tienen la extensión “.lua”.

Tutorial de Lua: ejecutar directamente código Lua usando un shebang

En sistemas operativos Linux / UNIX / macOS también es posible hacer que un archivo de código fuente en Lua sea directamente ejecutable. Para ello, introduce un hashbang (también llamado shebang) como primera línea en el archivo Lua:

#!/usr/local/bin/lua
-- Código Lua para ejecutar

Como puedes ver, el hashbang contiene la ubicación del archivo binario de Lua, que en nuestro caso es “#!/usr/local/bin/lua”. En algunas ocasiones, la ubicación en tu sistema podría diferir de la que se indica, pero en tal caso podrás indicar la ubicación correcta del archivo binario. Para hacerlo, introduce la orden “which”en la línea de comandos:

# Indicar la ubicación del archivo binario de Lua
which lua

Una vez hayas añadido el hashbang al código Lua, marca el archivo para indicar que puede ser ejecutado por el usuario. Para ello, introduce la siguiente orden en la línea de comandos: 

# Marcar archivo Lua como ejecutable 
chmod u+rx <nombre del archivo>.lua

A continuación, ejecuta el código Lua en el directorio actual:

./<nombre del archivo>.lua
Consejo

El truco del hashbang funciona en Linux y en sistemas tipo UNIX, como macOS, con la mayoría de lenguajes de scripting. También se puede aplicar el mismo proceso a códigos Ruby o Python para hacerlos ejecutables.

Aprender las bases de Lua

Lua es un lenguaje multiparadigma y con un estilo de base imperativo y funcional. Se trata de un lenguaje totalmente dinámico, por lo que no distingue entre “compile time” y “run time”. Además, apuesta sin excepción por una gestión dinámica de la memoria, es decir, permite que el tamaño de un objeto almacenado cambie durante el tiempo de ejecución. De eliminar datos innecesarios para liberar espacio se encarga un garbage collector (GC), así los programadores pueden dedicarse a otras tareas.

Cómo utilizar los comentarios en el código Lua

Los comentarios son un elemento esencial de cualquier lenguaje de programación. Estas son algunas de sus funciones más importantes:

  • Esbozar partes del código
  • Documentar características del código
  • Activar o desactivar líneas de código

En Lua, un comentario de una sola línea comienza con dos guiones (“--”) y acaba donde acaba la línea:

-- Esta línea es ignorada por el intérprete
***CODE***
Para escribir comentarios que ocupen más de una línea, se usa una combinación de guiones y corchetes (“--[[”  y “]]”):
***CODE***
--[[
Este comentario 
ocupa más 
de una línea
]]

Tutorial de Lua: valores y tipos

Lua, al igual que la mayoría de lenguajes de scripting, es un lenguaje tipificado dinámicamente, es decir, un lenguaje en el que los tipos no están asociados a variables, sino a valores. Cada valor tiene un solo tipo asociado, ya sea número, secuencia de caracteres, valor lógico, etc. En total, Lua comprende una cantidad no muy abrumadora de tipos, que hemos resumido en la siguiente tabla:

Tipo

Explicación

number

Número decimal

string

Secuencia de caracteres

boolean

Valor lógico: “true” o “false”

nil

Falta este valor; el tipo solo tiene el valor “nil”

function

Función

table

Tipo de dato compuesto: “List” / “Array”, “Hash” / “Dictionary”

thread

Corrutina

userdata

Tipo de dato C definido por el usuario

Lua puede aplicar sintaxis literal a los valores de todos los tipos, a excepción de “thread” y “userdata”. Para indicar el tipo de un valor se utiliza la función “type()”, que devuelve el nombre del tipo en forma de string (es decir, como una secuencia de caracteres). A continuación presentamos un par de ejemplos:

type(42) -- El tipo es `number`
type("Lua Tutorial") -- El tipo es `string`
type(false) -- El tipo es `boolean`
type(var) -- El tipo es `nil`, ya que `var` no está definido
Nota

Ten en cuenta que, como podrás ver en los siguientes ejemplos, Lua no asigna el índice 0 al primer elemento de una lista (como sí hacen la mayoría de lenguajes), ¡sino el índice 1!

Programar en Lua: ¿qué son las expresiones, las variables y los operadores?

Las expresiones (expressions) son evaluadas por el intérprete y devuelven un valor como resultado. Además, combinan literales, operadores, variables y llamadas de función; y pueden agruparse, si se quiere, entre paréntesis “()”. Puesto que se trata de un lenguaje dinámico, Lua muestra automáticamente el tipo del valor de retorno. Estos son algunos ejemplos de expresiones:

-- Operación aritmética en Lua
1 + 2 -- evaluado como valor `3`
-- Concatenación de strings en Lua
'Walther' .. 'White' -- evaluado como `WaltherWhite`
-- Concatenación de strings en Lua con integración automática de un número
'Los ' .. 3 .. ' Mosqueteros' -- evaluado como `Los 3 Mosqueteros`
-- Test de igualdad en Lua
7 == '7' – evaluado como `false`
-- Test de igualdad en Lua
'pequeño' == string.lower('PEQUEÑO') -- evaluado como `true`
-- Información dinámica de tipo
type(var) == 'nil' -- evaluado como `true`, ya que `var` no está definido

Una variable es el nombre de un valor en la memoria. Al igual que en la mayoría de lenguajes de programación, en Lua los nombres empiezan con una letra o un guion bajo (_) al que siguen otras letras, guiones bajos o cifras. La distinción entre letras en mayúscula o en minúscula es muy importante en estos nombres. Sin embargo, ciertas palabras se reservan para usos concretos y no pueden usarse, por sí solas, como nombres:

and, end, in, repeat, break, false, local, return, do, for, nil, then, else, function, not, true, elseif, if, or, until, while

Estas palabras sí pueden usarse, en cambio, como parte de un nombre más largo:

for = "Peter" -- Genera un error
for_user = "Peter" -- Permitido

La asignación de un valor a una variable se realiza mediante el operador de asignación (=), que no debe confundirse con el operador lógico de igualdad (==). Como es habitual, al asignar valores se distingue entre “L-value” y “R-value”: la variable debe estar en el lado izquierdo del operador de asignación, por lo que pasa a ser el “L-value”. A dicho valor se le asigna el valor evaluado del “R-value”, que está a la derecha del operador.

cantidad = 13 -- Está bien
13 = cantidad -- Genera un error porque al valor 13 no puede asignársele ningún valor 

Los operadores (operators) generan un nuevo valor a partir de uno o más operandos. Existen operadores unarios (que solo necesitan un argumento) y binarios (que necesitan dos). Su función consiste en conectar operandos de un tipo concreto y devolver un valor de un tipo concreto. A continuación, presentamos los diferentes operadores que encontramos en Lua.

Los operadores aritméticos operan con cifras y devuelven otra cifra:

Operador aritmético

Aridad

Operación

+

binario

Suma

-

binario

Resta

*

binario

Multiplicación

/

binario

División

%

binario

Operación módulo (residuo)

^

binario

Potencia

-

unario

Negación

Los operadores relacionales, todos ellos binarios, comprueban cómo reaccionan dos operandos entre sí y devuelven un valor lógico

Operador relacional

Prueba

==

Igualdad

~=

Desigualdad

>

Mayor que

<

Menor que

>=

Mayor que o igual

<=

Menor que o igual

Los operadores lógicos asocian valores lógicos y devuelven otro valor lógico:

Operador lógico

Aridad

Operación

and

binario

Asociación “y”

or

binario

Asociación “o”

not

unario

Negación

Además de los operadores mencionados, en Lua existen dos operadores especiales que sirven para concatenar strings (es decir, para encadenar secuencias de caracteres) y para indicar el tamaño de un valor compuesto, como puede ser “table” o “string”: 

Operador

Aridad

Operación

..

binario

Concatenación de strings

#

unario

Indicar el número de elementos de una tabla / la longitud de una string

En Lua no se utilizan operadores de asignación compuestos, como lo son, por ejemplo, “+=” y “-=” en muchos otros lenguajes de scripting. Para incrementar y decrementar variables, por lo tanto, se redacta explícitamente la operación:

precio = 42.99
descuento = 0.15 -- 15% descuento
precio -= precio * descuento -- `-=` no funciona en Lua
-- El decremento ha de redactarse explícitamente 
precio = precio – (precio * descuento)

Tutorial de Lua: ámbitos de validez y bloques

El concepto de ámbito de validez es clave en todos los lenguajes de programación, ya que las variables solo existen dentro de un ámbito de validez determinado. Al igual que en JavaScript, las variables en Lua son globales en la configuración por defecto. Sin embargo, el uso continuo de variables globales es lo que se conoce como antipatrón o anti-pattern y debería evitarse. Para conseguirlo, en Lua existe la palabra clave “local”, que limita el ámbito de validez de una variable al bloque que la rodea. Se trata de un efecto similar al que provoca “let” en JavaScript.

-- Esta variable es global
x = 5
-- Definir variable local
local z = 10

En Lua, los cuerpos de las funciones y los bucles abren un nuevo ámbito de validez. Lua utiliza, además, los llamados bloques explícitos. Cada bloque define un nuevo ámbito de validez para el código ubicado entre las palabras clave “do” y “end”. Estas palabras corresponden, respectivamente, a las llaves de apertura y de cierre “{” y “}” en Java / C / C++. El siguiente ejemplo de código muestra cómo están relacionados entre ellos los bloques, los ámbitos de validez y las variables:

-- Ámbito de validez exterior
do
  local x = 1
  do -- Ámbito de validez interior
    local y = 2
    -- Generar `z` en el ámbito de validez global
    -- Con acceso a la variable local `x` del ámbito de validez exterior
    -- y a la variable local `y` del ámbito de validez interior
    z = x + y -- `z` tiene ahora el valor `3`
  end
  print(x) -- resulta en `1` 
  print(y) -- resulta en `nil`, porque `y` no existe en el ámbito de validez exterior 
  print(z) -- resulta en `3` 
end
-- `z` es global y existe, por lo tanto, fuera del ámbito de validez exterior 
z = z + 4
print(z) -- resulta en `7`

Programar en Lua con estructuras de control

Lua también reconoce las estructuras de control habituales que se encuentran en otros lenguajes de programación, entre las que se encuentran las bifurcaciones y los bucles. A continuación, presentamos un ejemplo con órdenes “if”, “then”, “else” y “elseif” en Lua:

límite = 42;
número = 43;
if número < límite then
  print("Por debajo del límite.")
elseif número == límite then
  print("Justo en el límite…")
else
  print("¡Por encima del límite!")
end

Además del típico bucle con “while”, Lua también admite su contraparte, el bucle con “repeat” y “until”, que también se usa en Ruby y que requiere que se invierta la condición aplicada. Eso significa que un “while” con la condición “número </= límite” se corresponde con un “repeat”-“until” con la condición “número > límite”. ¡Ten cuidado con la orden “repeat”! Independientemente de la condición, el cuerpo del bucle se ejecutará al menos una vez. He aquí un ejemplo:

límite = 10
número = 1
while número <= límite do
  print("Número:", número)
  número = número + 1
end
-- Atención: aunque `número` ya es mayor que `límite`,
-- el cuerpo del bucle se ejecuta una vez
número = 11
repeat 
  print("Número:", número)
 número = número + 1
until número > límite

Tal y como ocurre con la mayoría de lenguajes imperativos, Lua no solo reconoce el bucle con “while”, sino también una orden con “for”, que puede adoptar dos formas: una variante similar a la del lenguaje C, con variable de bucle, y una variante con iterador. En el siguiente ejemplo mostramos el uso de la orden “for” con variable de bucle:

inicio = 1
fin = 10
for número = inicio, ende do
  print("Número actual:", número) -- `1,2,3,4,5,6,7,8,9,10`
end
-- Poner `paso` explícitamente a `2`
paso = 2
for número = inicio, fin, paso do
  print("Número actual:", número) -- `1,3,5,7,9`
end
-- El paso también puede ser negativo
paso = -2
-- Con paso negativo, invertir inicio y fin, para contar de forma descendente
for número = fin, inicio, paso do
  print("Número actual", número) -- `10,8,6,4,2`
end

Sorprendentemente, la variable de bucle definida en el bucle de “for” es local, no global, sin necesidad de haber sido declarada explícitamente como “local”. Esto resulta muy conveniente y es uno de los aspectos en los que Lua aventaja a JavaScript. En este último lenguaje, toda variable de bucle que no esté declarada con “let” o “var” es global, lo cual puede provocar graves fallos.

Ahora, nos ocuparemos del bucle de “for” con iterador en Lua. En principio, el concepto es similar al de Python: en lugar de incrementar una variable de bucle y usarla como índice (“index”) en una lista, se itera directamente a través de los elementos de la lista. Para generar el iterador suele usarse la función “ipairs()”, como muestra el siguiente ejemplo:

-- Definir lista de años
décadas = {1910, 1920, 1930, 1940, 1950, 1960, 1970, 1980, 1990}
-- Acceder a años concretos usando el iterador 
for index, año in ipairs(décadas) do
  print(index, año)
end

Más funciones en Lua

En Lua, al igual que en C / C++, Java y JavaScript, las funciones se definen con la palabra clave “function”. Como es habitual, tras el nombre de la función aparecen los parámetros correspondientes entre paréntesis. Lo que caracteriza a Lua en este sentido es que en la llamada de función pueden omitirse los paréntesis con tan solo un literal como argumento. Una función de Lua no ha de devolver necesariamente un valor. En caso de que no haya valor, se habla por definición de un “procedimiento”:

-- Definir procedimiento
function hola(nombre)
  print("Buenos días", nombre)
end
-- Llamar función
hola("señor")
-- Esto también es posible:
hola "señor"
-- Lo siguiente, en cambio, no funciona: 
nombre = "Peter"
hola nombre -- Error de sintaxis
-- Con variable en lugar de literal, la función debe ser llamada con paréntesis 
hola(nombre)

Para obtener de vuelta un valor a partir de una función, se usa, como es habitual, la palabra clave “return”. Esta palabra finaliza la ejecución de la función y hace que se vuelva a mostrar el valor indicado. En el siguiente ejemplo de código hemos obtenido el cuadrado de un número:

-- Función con un único valor de retorno
function cuadrado(número)
  -- La expresión `número * número` se evalúa
  -- y vuelve a mostrarse su valor
  return número * número
end
-- Llevar un número a su cuadrado
print(cuadrado(9)) -- `81`

Al igual que en Python y JavaScript, en Lua una función puede aceptar una cantidad variable de argumentos. Los argumentos se guardan en el constructo especial “(...)”. Para acceder a ellos, suele ser útil resumirlos con la expresión “{...}” en una lista. Otra manera de hacerlo es usando la función “select()”, que extrae un argumento del índice indicado. Para indicar el número de argumentos se utiliza la expresión “#{...}”.

-- Activar todos los argumentos de una función
function var_args(...)
  for index, arg  in ipairs({...}) do
    print(index, arg)
  end
end
var_args('Peter', 42, true)

Además de aceptar un número variable de argumentos, Lua también permite que se devuelvan varios valores con una orden “return”. Esta opción es similar a la que ofrece Python, pero sin el tipo explícito “Tupel”. Otro punto en común entre Lua y Python es que en ambos es habitual asignar los valores de retorno a más de una variable al llamar la función. He aquí un ejemplo: 

-- Función con varios valores de retorno
function primero_y_último(lista)
  -- Devolver el primer y el último elemento de la lista 
  -- Los valores de retorno están separados por comas `,` 
  return lista[1], lista[#lista]
end
personas = {"Jim", "Jack", "John"}
-- Asignación de los valores de retorno a más de una variable 
primero, último = primero_y_último(personas)
print("El primero es", primero)
print("El último es", último)

Si alguno de los valores de retorno no es necesario, por lo general se usa el guion bajo (_) como marcador de posición, como podemos ver en el siguiente ejemplo:

function min_med_max(...)
  -- Indicar valores iniciales para `min` y `max` en el primer argumento
  local min = select(1, ...)
  local max = select(1, ...)
  -- Poner el valor medio a 0 inicialmente
  local med = 0
  -- Iterar a través de los números
  -- No necesitamos la variable índice (`index`),
  -- por lo que usamos `_` como marcador de posición
  for _, número  in ipairs({...}) do
    -- Establecer un nuevo mínimo si es necesario
    if min > número then
      min = número
    end
    -- Establecer un nuevo máximo si es necesario
    if max < número then
      max = número
    end
    -- Sumar números para la media aritmética
    med = med + número
  end
  -- Dividir la suma de los números entre la cantidad de números
  med = med / #{...}
  return min, med, max
end
-- Aquí no necesitamos el valor `med`,
-- por lo que usamos `_` como marcador de posición
min, _, max = min_med_max(78, 34, 91, 7, 28)
print("El mínimo y el máximo de los números son", min, max)

En Lua, las funciones son first-class citizens, es decir, pueden asociarse a variables y, así, pueden transferirse también como argumento a otras funciones. Además, una función en Lua puede funcionar como valor de retorno de otra función. Gracias a todas estas posibilidades, Lua hace posible una programación funcional, que ilustramos a continuación con el ejemplo de la famosa función “map()”:

-- La función `map()` en Lua
-- adopta, como argumentos, una función `f` y una lista
function map(f, lista)
  -- Crear una nueva lista para los valores resultantes
  local _lista = {}
  -- Iterar con índice a través de los elementos de la lista
  for index, valor in ipairs(lista) do
    -- Aplicar la función `f()` al valor actual de la lista
    -- y guardar el valor de retorno en la nueva lista, con el mismo índice 
    _lista[index] = f(valor)
  end
  -- Devolver lista nueva
  return _lista
end
-- Lista de números
números = {3, 4, 5}
-- Función que se aplica a todos los elementos de la lista 
function cuadrado(número)
  return número * número
end
-- Generar los cuadrados mediante la función `map()`
cuadrados = map(cuadrado, números) -- `{9, 16, 25}`
-- Mostrar números al cuadrado
for _, número in ipairs(cuadrados) do
  print(número)
end

En la programación funcional es habitual el uso de la recursividad, es decir, que una función se llame a sí misma una y otra vez con argumentos modificados. En el caso de Lua, debemos tener especial cuidado con las funciones llamadas de forma recursiva, que debemos declarar explícitamente como “local”.

function f()
  -- Llamada recursiva
  f() -- Dado el caso, remite a la variable global `f`
end
-- En lugar de eso
local function f()
  -- Llamada recursiva
  f() -- Remite a la función circundante
end
-- Equivalente a
local f; -- Declarar la variable `f` explícitamente como `local` 
f = function() -- Asignación de la función a la variable local `f`
  f() -- Remite seguro a la función circundante
end