Tablas en Lua
Las tablas son el corazón de las estructuras de datos en Lua. A diferencia de otros lenguajes que ofrecen múltiples tipos de estructuras (arrays, listas, diccionarios, conjuntos), Lua proporciona una única estructura versátil y poderosa: la tabla. Esta simplicidad no limita las posibilidades, sino que ofrece una gran flexibilidad para construir cualquier tipo de estructura de datos que necesites.
En este artículo aprenderás qué son las tablas, cómo crearlas y manipularlas, y cómo pueden servir de base para implementar estructuras de datos más complejas como arrays o diccionarios.
¿Qué son las tablas?
Las tablas (tables en inglés) son colecciones de pares clave/valor donde:
- Las claves pueden ser de cualquier tipo excepto
nilyNaN(not a number) - Los valores pueden ser de cualquier tipo, incluyendo
nil, otras tablas, funciones, etc. - No tienen un tamaño fijo: crecen dinámicamente según las necesidades
- Son la única estructura de datos nativa en Lua
Piensa en una tabla como un contenedor flexible que puede funcionar como:
- Un array (lista ordenada de elementos)
- Un diccionario (pares clave-valor)
- Un objeto (con propiedades y métodos)
- Una combinación de todo lo anterior
Creación de tablas
Tabla vacía
La forma más simple de crear una tabla es usando llaves vacías:
-- Crear una tabla vacía
miTabla = {}
print(type(miTabla)) --> table
Cuando imprimes una tabla directamente, Lua muestra su tipo y una dirección de memoria única:
print(miTabla) --> table: 0x55b2e8c04230
Tabla con valores iniciales
Puedes inicializar una tabla con valores desde su creación:
-- Tabla como array (índices numéricos implícitos)
frutas = {"manzana", "naranja", "plátano"}
-- Tabla como diccionario (claves explícitas)
persona = {nombre = "Ana", edad = 25, ciudad = "Madrid"}
-- Tabla mixta (índices numéricos y claves)
datos = {10, 20, total = 30, activo = true}
Inserción de elementos
Una vez creada una tabla, puedes añadir elementos de diversas formas.
Sintaxis con corchetes
Usa corchetes [] para asignar valores a cualquier tipo de clave:
tabla = {}
-- Clave de tipo string
tabla["nombre"] = "Lua"
-- Clave de tipo number
tabla[1] = 100
tabla[2] = 200
-- Recuperar valores
print(tabla["nombre"]) --> Lua
print(tabla[1]) --> 100
Sintaxis simplificada para strings
Para claves que son strings válidos como identificadores (letras, números, guión bajo, sin comenzar con número), puedes usar la notación de punto:
tabla = {}
-- Estas dos formas son equivalentes
tabla.lenguaje = "Lua"
tabla["lenguaje"] = "Lua"
print(tabla.lenguaje) --> Lua
print(tabla["lenguaje"]) --> Lua
Restricción importante: La notación con punto solo funciona con identificadores válidos:
tabla.version = "5.4" -- Correcto
tabla._privado = "dato" -- Correcto
tabla.2datos = "valor" -- Error: no puede comenzar con número
-- Para claves que no son identificadores válidos, usa corchetes
tabla["2datos"] = "valor" -- Correcto
tabla["dato con espacios"] = "ok" -- Correcto
Tipos de claves permitidos
Las claves en Lua pueden ser de prácticamente cualquier tipo, lo que proporciona gran flexibilidad.
Claves de diferentes tipos
tabla = {}
-- Clave string
tabla["texto"] = "valor1"
-- Clave numérica
tabla[42] = "valor2"
-- Clave booleana
tabla[true] = "valor3"
-- Clave tabla (otra tabla como clave)
otraTabla = {}
tabla[otraTabla] = "valor4"
-- Clave función
miFuncion = function() end
tabla[miFuncion] = "valor5"
-- Recuperar valores
print(tabla["texto"]) --> valor1
print(tabla[42]) --> valor2
print(tabla[true]) --> valor3
print(tabla[otraTabla]) --> valor4
print(tabla[miFuncion]) --> valor5
Claves basadas en expresiones
Las claves también pueden ser el resultado de evaluar expresiones:
tabla = {}
-- Resultado de operación aritmética
tabla[10 + 5] = "quince"
print(tabla[15]) --> quince
-- Resultado de función
tabla[string.upper("clave")] = "MAYÚSCULAS"
print(tabla["CLAVE"]) --> MAYÚSCULAS
-- Resultado de concatenación
prefijo = "dato"
tabla[prefijo .. "_1"] = "primer dato"
print(tabla["dato_1"]) --> primer dato
Claves no permitidas
Solo hay dos valores que no pueden ser claves:
tabla = {}
-- nil como clave produce error
tabla[nil] = "valor"
-- Error: table index is nil
-- NaN (Not a Number) como clave produce error
tabla[0/0] = "valor"
-- Error: table index is NaN
Acceso a elementos
Recuperar valores existentes
Usa la misma sintaxis que para insertar:
config = {
host = "localhost",
puerto = 8080,
["base-datos"] = "usuarios"
}
print(config.host) --> localhost
print(config.puerto) --> 8080
print(config["base-datos"]) --> usuarios
Acceso a claves inexistentes
Cuando intentas acceder a una clave que no existe, Lua devuelve nil sin generar error:
tabla = {a = 1, b = 2}
print(tabla.a) --> 1
print(tabla.c) --> nil (la clave 'c' no existe)
-- Útil para verificar existencia
if tabla.c == nil then
print("La clave 'c' no existe")
end
Modificación de valores
Modificar un valor existente es tan simple como una nueva asignación:
datos = {
nombre = "Juan",
edad = 30
}
print(datos.edad) --> 30
-- Modificar el valor
datos.edad = 31
print(datos.edad) --> 31
-- Cambiar también el tipo de valor
datos.edad = "treinta y uno"
print(datos.edad) --> treinta y uno
Eliminación de elementos
Para eliminar un par clave/valor de una tabla, asigna nil a esa clave:
usuario = {
nombre = "María",
email = "maria@example.com",
temporal = "dato borrable"
}
print(usuario.temporal) --> dato borrable
-- Eliminar el elemento
usuario.temporal = nil
print(usuario.temporal) --> nil
-- La clave ya no existe en la tabla
for clave, valor in pairs(usuario) do
print(clave, valor)
end
-- Salida:
-- nombre María
-- email maria@example.com
-- (temporal no aparece)
Tablas como arrays
Aunque Lua no tiene un tipo array específico, las tablas pueden funcionar perfectamente como arrays usando índices numéricos consecutivos.
Importante: En Lua, los arrays comienzan en índice 1, no en 0 como en muchos otros lenguajes.
-- Crear un array
numeros = {10, 20, 30, 40, 50}
-- Acceder por índice (comienza en 1)
print(numeros[1]) --> 10
print(numeros[2]) --> 20
print(numeros[5]) --> 50
-- Añadir elementos
numeros[6] = 60
print(numeros[6]) --> 60
-- Obtener longitud con operador #
print(#numeros) --> 6
Recorrer un array
colores = {"rojo", "verde", "azul"}
-- Usando for numérico
for i = 1, #colores do
print(i, colores[i])
end
-- Usando ipairs (específico para arrays)
for indice, color in ipairs(colores) do
print(indice, color)
end
Tablas como diccionarios
Las tablas también pueden funcionar como diccionarios (también llamados mapas o arrays asociativos), donde las claves son strings u otros tipos:
traduccion = {
hola = "hello",
adios = "goodbye",
gracias = "thank you"
}
print(traduccion.hola) --> hello
print(traduccion["adios"]) --> goodbye
-- Añadir nuevas traducciones
traduccion.por_favor = "please"
traduccion["de_nada"] = "you're welcome"
Recorrer un diccionario
capitales = {
Espana = "Madrid",
Francia = "París",
Italia = "Roma"
}
-- Usar pairs para recorrer todos los elementos
for pais, capital in pairs(capitales) do
print("La capital de " .. pais .. " es " .. capital)
end
Tablas mixtas
Una tabla puede contener tanto índices numéricos como claves string simultáneamente:
estudiante = {
"Juan", -- índice 1
"García", -- índice 2
edad = 20, -- clave "edad"
curso = "2º", -- clave "curso"
"Matemáticas" -- índice 3
}
print(estudiante[1]) --> Juan
print(estudiante[3]) --> Matemáticas
print(estudiante.edad) --> 20
print(estudiante.curso) --> 2º
Operador de longitud
El operador # devuelve la longitud de la parte array de una tabla (secuencia de índices numéricos consecutivos comenzando en 1):
lista = {10, 20, 30, 40}
print(#lista) --> 4
-- Añadir elemento al final
lista[#lista + 1] = 50
print(#lista) --> 5
Advertencia: El operador # solo cuenta elementos con índices numéricos consecutivos desde 1:
tabla = {10, 20, 30}
tabla[5] = 50 -- Rompe la secuencia
print(#tabla) --> 3 (solo cuenta hasta donde hay secuencia continua)
Para tablas tipo diccionario, # no es útil. Debes recorrerlas con pairs() para contar elementos.
Tablas anidadas
Las tablas pueden contener otras tablas, permitiendo crear estructuras complejas:
empresa = {
nombre = "TechCorp",
empleados = {
{nombre = "Ana", puesto = "Desarrolladora"},
{nombre = "Luis", puesto = "Diseñador"},
{nombre = "Carlos", puesto = "Manager"}
},
direccion = {
calle = "Gran Vía",
numero = 123,
ciudad = "Madrid"
}
}
-- Acceder a datos anidados
print(empresa.nombre) --> TechCorp
print(empresa.empleados[1].nombre) --> Ana
print(empresa.direccion.ciudad) --> Madrid
Inicialización completa
Puedes crear y llenar una tabla completamente en una sola expresión:
-- Array con valores iniciales
numeros = {5, 10, 15, 20}
-- Diccionario con claves y valores
config = {
titulo = "Mi Aplicación",
version = "1.0",
depuracion = true
}
-- Tabla mixta
datos = {
100, -- índice 1
200, -- índice 2
nombre = "Lua",
activo = true,
300 -- índice 3
}
-- Con claves explícitas usando corchetes
especial = {
[1] = "primero",
["clave especial"] = "valor",
[true] = "booleano como clave"
}
Tablas y referencias
Concepto importante: Cuando asignas una tabla a una variable, no copias la tabla, sino que creas una referencia a ella:
tabla1 = {a = 1, b = 2}
tabla2 = tabla1 -- tabla2 es una referencia a la misma tabla
-- Modificar a través de tabla2 afecta a tabla1
tabla2.a = 100
print(tabla1.a) --> 100
print(tabla2.a) --> 100
-- Ambas variables apuntan a la misma tabla
print(tabla1 == tabla2) --> true
Para crear una copia independiente, debes copiar elemento por elemento:
original = {a = 1, b = 2, c = 3}
copia = {}
for clave, valor in pairs(original) do
copia[clave] = valor
end
-- Ahora son tablas independientes
copia.a = 100
print(original.a) --> 1
print(copia.a) --> 100
Ejemplos prácticos
Contador de palabras
function contarPalabras(texto)
local contador = {}
for palabra in string.gmatch(texto, "%w+") do
palabra = string.lower(palabra)
contador[palabra] = (contador[palabra] or 0) + 1
end
return contador
end
texto = "Lua es genial Lua es poderoso"
resultado = contarPalabras(texto)
for palabra, cantidad in pairs(resultado) do
print(palabra .. ": " .. cantidad)
end
-- Salida:
-- lua: 2
-- es: 2
-- genial: 1
-- poderoso: 1
Lista de tareas
tareas = {
{texto = "Estudiar Lua", completada = false},
{texto = "Hacer ejercicios", completada = false},
{texto = "Crear proyecto", completada = false}
}
-- Marcar segunda tarea como completada
tareas[2].completada = true
-- Listar tareas pendientes
print("Tareas pendientes:")
for i, tarea in ipairs(tareas) do
if not tarea.completada then
print(i .. ". " .. tarea.texto)
end
end
Agenda de contactos
agenda = {}
-- Añadir contactos
function agregarContacto(nombre, telefono, email)
agenda[nombre] = {
telefono = telefono,
email = email
}
end
agregarContacto("Ana", "600123456", "ana@example.com")
agregarContacto("Luis", "600789012", "luis@example.com")
-- Buscar contacto
function buscarContacto(nombre)
if agenda[nombre] then
print("Nombre: " .. nombre)
print("Teléfono: " .. agenda[nombre].telefono)
print("Email: " .. agenda[nombre].email)
else
print("Contacto no encontrado")
end
end
buscarContacto("Ana")
Resumen
Las tablas son la estructura de datos fundamental en Lua. En este artículo has aprendido:
- Las tablas son colecciones de pares clave/valor muy flexibles
- Se crean con llaves
{}y pueden inicializarse vacías o con valores - Las claves pueden ser de casi cualquier tipo (excepto
nilyNaN) - Puedes acceder a elementos con corchetes
[]o punto.(para strings) - Asignar
nila una clave elimina ese elemento - Las tablas pueden funcionar como arrays, diccionarios o ambos
- El operador
#devuelve la longitud de la parte array - Las asignaciones crean referencias, no copias
- Las tablas pueden anidarse para crear estructuras complejas
En los siguientes artículos profundizaremos en estructuras específicas como arrays y diccionarios, y aprenderás a manipular tablas con las funciones de la librería table. Las tablas son una herramienta poderosa que te acompañará en todo tu aprendizaje de Lua.