cv.cm

Programación · Lenguajes de programación

Tutorial de Lua para principiantes

8 min de lectura

Respuesta corta

Lua es un lenguaje de scripting ligero e incrustable que se usa en videojuegos, Roblox (mediante su dialecto Luau), Neovim, Redis y dispositivos embebidos. Tiene una sintaxis reducida y una única estructura de datos, la tabla, que sirve como arreglo, diccionario y objeto. Esta página cubre la instalación de Lua, las variables y los tipos, el control de flujo, las funciones, las tablas, las metatablas, el manejo de errores con pcall y un ejemplo completo.

Qué es Lua y para qué se usa

Lua es un lenguaje de scripting pequeño, rápido y de tipado dinámico creado en 1993 en la PUC-Rio, una universidad de Brasil. Todo su intérprete es una biblioteca compacta escrita en C, lo que facilita incrustarlo en programas más grandes. Tiene una sola estructura de datos, la tabla, y muy pocos conceptos, por lo que se aprende rápido.

Lua se usa sobre todo como lenguaje de scripting incrustado: motores de videojuegos y videojuegos (Roblox usa un dialecto de Lua llamado Luau; LÖVE y Defold usan Lua), el editor Neovim, la base de datos Redis, herramientas de red como Nginx con OpenResty y muchos dispositivos embebidos. LuaJIT es un popular compilador just-in-time para código Lua 5.1.

Instala Lua y ejecuta Hola, mundo

Instala Lua con tu gestor de paquetes (por ejemplo brew install lua o apt install lua5.4) o compílalo desde el código fuente en lua.org. Ejecuta lua sin argumentos para abrir un prompt interactivo.

-- hello.lua
print("Hello, world!")
lua hello.lua
# Hello, world!

Los comentarios empiezan con --. Un comentario de bloque se escribe --[[ ... ]]. Las instrucciones no necesitan punto y coma.

Variables y tipos básicos

Las variables son globales por defecto. Declara siempre las variables con local, a menos que de verdad quieras una global. Lua tiene ocho tipos básicos; los que usas a diario están a continuación.

local name = "Lua"
local year = 1993              -- integer
local pi = 3.14159             -- float
local ok = true
local nothing = nil

print(type(name), type(year), type(nothing))  -- string  number  nil
print(math.type(year), math.type(pi))         -- integer  float
print(name .. " " .. year)                    -- Lua 1993 (.. joins strings)
print(#name)                                  -- 3 (length)
print(7 / 2, 7 // 2, 7 % 2, 2 ^ 10)           -- 3.5  3  1  1024.0
print(string.format("%.2f", pi))              -- 3.14
print(tonumber("42") + 1, tostring(10))       -- 43  10
TipoEjemploNotas
nilnilausencia de valor; las variables sin asignar son nil
booleantrue, falsesolo false y nil son falsos; 0 y "" son verdaderos
number42, 3.14subtipos entero y flotante desde Lua 5.3
string"hi", 'hi', [[long]]cadenas de bytes inmutables
table{1, 2, x = 3}la única estructura de datos
functionfunction() endvalores de primera clase

El operador de desigualdad es ~=, no !=. Los operadores lógicos son las palabras and, or y not.

Control de flujo

Los bloques terminan con la palabra clave end. Lua tiene if/elseif/else, while, repeat ... until, un for numérico y un for genérico que funciona con iteradores.

local score = 72
if score >= 90 then
  print("A")
elseif score >= 70 then
  print("B")                   -- B
else
  print("C")
end

for i = 1, 3 do                -- 1 to 3, inclusive
  io.write(i, " ")             -- 1 2 3
end
print()

for i = 10, 1, -4 do io.write(i, " ") end   -- 10 6 2
print()

local n = 3
while n > 0 do n = n - 1 end

repeat
  n = n + 1
until n >= 2
print(n)                       -- 2

No existe la instrucción continue; usa goto continue con una etiqueta ::continue::, o reestructura el bucle con un if.

Funciones

Las funciones son valores. Pueden recibir un número variable de argumentos con ... y devolver varios valores. Las clausuras capturan variables locales del ámbito que las contiene.

local function add(a, b)
  return a + b
end

local function minmax(t)
  local lo, hi = math.huge, -math.huge
  for _, v in ipairs(t) do
    lo, hi = math.min(lo, v), math.max(hi, v)
  end
  return lo, hi                -- two return values
end

local function counter()
  local count = 0
  return function()            -- a closure
    count = count + 1
    return count
  end
end

print(add(2, 3))               -- 5
local lo, hi = minmax({4, 9, 1})
print(lo, hi)                  -- 1  9
local next_id = counter()
print(next_id(), next_id())    -- 1  2
local function sum(...)
  local total = 0
  for _, v in ipairs({...}) do total = total + v end
  return total
end
print(sum(1, 2, 3))            -- 6

Tablas: arreglos, diccionarios y objetos

Una tabla es un arreglo asociativo que puede indexarse con cualquier valor excepto nil. La misma estructura sirve como arreglo, diccionario, conjunto, módulo u objeto. Por convención, los arreglos empiezan en el índice 1.

local fruits = {"apple", "banana"}
table.insert(fruits, "cherry")
print(#fruits, fruits[1])            -- 3  apple

for i, f in ipairs(fruits) do        -- array part, in order
  print(i, f)
end

local ages = {ann = 31, bo = 25}
ages["cy"] = 40
ages.bo = nil                        -- remove a key
for k, v in pairs(ages) do           -- all keys, order not defined
  print(k, v)
end

table.sort(fruits, function(a, b) return a > b end)
print(table.concat(fruits, ", "))    -- cherry, banana, apple

Metatablas y Lua orientado a objetos

Las metatablas son la característica distintiva de Lua. Una metatabla asociada a una tabla cambia su comportamiento: __index proporciona las claves que faltan (se usa para la herencia) y campos como __add, __eq y __tostring sobrecargan operadores. La sintaxis con dos puntos obj:method() pasa el objeto como primer parámetro oculto self.

local Vector = {}
Vector.__index = Vector

function Vector.new(x, y)
  return setmetatable({x = x, y = y}, Vector)
end

function Vector:length()
  return math.sqrt(self.x ^ 2 + self.y ^ 2)
end

Vector.__add = function(a, b) return Vector.new(a.x + b.x, a.y + b.y) end
Vector.__tostring = function(v) return "(" .. v.x .. ", " .. v.y .. ")" end

local v = Vector.new(3, 4) + Vector.new(0, 0)
print(tostring(v))       -- (3, 4)
print(v:length())        -- 5.0

Lua también tiene corrutinas (coroutine.create, coroutine.resume, coroutine.yield), que permiten que una función se pause y se reanude más tarde. Los scripts de videojuegos las usan a menudo para comportamientos paso a paso que abarcan varios fotogramas.

Manejo de errores

Lanza un error con error(message). Captúralo con pcall (llamada protegida), que devuelve false más el error en lugar de detener el programa. Muchas funciones de la biblioteca devuelven nil y un mensaje de error en lugar de lanzar un error, como hace io.open.

local function divide(a, b)
  if b == 0 then
    error("division by zero")
  end
  return a / b
end

local ok, result = pcall(divide, 10, 2)
print(ok, result)                  -- true  5.0

ok, result = pcall(divide, 1, 0)
print(ok, result)                  -- false  script.lua:3: division by zero

local f, err = io.open("missing.txt", "r")
if not f then print("open failed: " .. err) end

El nombre de archivo y el número de línea del mensaje de error dependen de dónde se ejecute el código. assert(value, message) lanza un error cuando value es false o nil y es una forma breve de comprobar resultados.

Un programa pequeño y completo: conteo de palabras

Este programa cuenta las palabras de un texto con una búsqueda de patrones, las ordena por frecuencia e imprime las tres primeras. Usa patrones de cadenas, tablas, ordenación con un comparador personalizado y formato de cadenas.

local text = [[
The cat and the dog. The dog sat,
and the cat ran.
]]

local counts = {}
for word in text:lower():gmatch("%a+") do
  counts[word] = (counts[word] or 0) + 1
end

local list = {}
for word, n in pairs(counts) do
  list[#list + 1] = {word = word, n = n}
end
table.sort(list, function(a, b)
  if a.n ~= b.n then return a.n > b.n end
  return a.word < b.word
end)

for i = 1, 3 do
  print(string.format("%-4s %d", list[i].word, list[i].n))
end
-- the  4
-- and  2
-- cat  2

Siguientes pasos

  • El Lua Reference Manual en lua.org, breve y completo.
  • Programming in Lua, de Roberto Ierusalimschy, uno de los creadores de Lua; la primera edición es gratuita en línea.
  • La documentación de tu entorno anfitrión, como la guía de Lua de Neovim, LÖVE o la documentación de Luau de Roblox.
  • LuaRocks, el gestor de paquetes para módulos de Lua.

Preguntas frecuentes

¿Por qué los arreglos de Lua empiezan en 1?

Los diseñadores de Lua eligieron la indexación desde 1 para ajustarse a las convenciones matemáticas y de las personas que no programan. Las funciones de la biblioteca estándar, como ipairs, # y table.insert, suponen que las secuencias empiezan en 1.

¿Lua es más rápido que Python?

El intérprete estándar de Lua es pequeño y, en general, rápido para un lenguaje dinámico, y LuaJIT es mucho más rápido en muchas cargas de trabajo. La velocidad real depende de la tarea, así que mide tu propio caso.

¿Cuál es la diferencia entre pairs e ipairs?

ipairs recorre la parte de arreglo en orden desde el índice 1 hasta el primer nil. pairs visita todas las claves de la tabla en un orden no especificado.

¿Qué es Luau?

Luau es un lenguaje derivado de Lua 5.1 y desarrollado por Roblox. Añade tipado gradual y mejoras de rendimiento, y se usa en los juegos de Roblox.

¿Lua es bueno para principiantes?

Sí. Tiene una sintaxis pequeña y coherente y pocos conceptos, por eso es popular para crear mods de videojuegos y para la enseñanza. Su biblioteca estándar mínima hace que dependas del programa anfitrión o de módulos para tener más funciones.

Todos los tutoriales de programación