Ir al contenido principal

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 nil y NaN (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 nil y NaN)
  • Puedes acceder a elementos con corchetes [] o punto . (para strings)
  • Asignar nil a 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.