Manipulación de tablas en Lua
Las tablas son la estructura de datos fundamental en Lua, y dominar su manipulación es esencial para escribir código eficiente y elegante. Lua proporciona una librería estándar llamada table con un conjunto de funciones diseñadas específicamente para facilitar las operaciones más comunes sobre tablas. Estas funciones nos permiten insertar, eliminar, ordenar, concatenar y mover elementos de manera sencilla, sin necesidad de implementar estas operaciones desde cero.
En este artículo exploraremos en detalle cada una de las funciones de la librería table, comprendiendo no solo su sintaxis, sino también sus casos de uso prácticos y las mejores formas de aplicarlas en situaciones reales. Aprenderás a trabajar con listas de forma profesional, optimizando tu código y evitando errores comunes.
La librería table
Todas las funciones que veremos están contenidas en el módulo table de la librería estándar de Lua. Este módulo se carga automáticamente cuando ejecutas el intérprete, por lo que puedes usar sus funciones directamente sin necesidad de importar nada adicional. Las funciones están optimizadas para trabajar con la parte de array de las tablas (elementos con índices numéricos consecutivos comenzando en 1).
A continuación examinaremos las funciones más importantes:
table.concat()- concatenación de elementostable.insert()- inserción de elementostable.remove()- eliminación de elementostable.move()- copia de elementos entre tablastable.sort()- ordenación de elementostable.unpack()- extracción de elementostable.pack()- empaquetado de valores
Función table.concat
La función concat permite unir todos los elementos de una tabla en una única cadena de texto. Es especialmente útil cuando necesitas construir cadenas a partir de múltiples fragmentos, ya que es mucho más eficiente que concatenar con el operador .. en un bucle.
Signatura:
table.concat(lista [, separador [, inicio [, fin]]])
Parámetros:
- lista: tabla cuyos elementos se concatenarán. Todos los elementos deben ser cadenas o números.
- separador (opcional): cadena que se insertará entre cada elemento. Si no se especifica, se usa la cadena vacía por defecto.
- inicio (opcional): índice del primer elemento a concatenar. Por defecto es 1.
- fin (opcional): índice del último elemento a concatenar. Por defecto es
#lista.
Retorna: una cadena de texto con todos los elementos concatenados.
Ejemplo básico:
frutas = {"manzana", "naranja", "platano", "uva"}
-- Concatenacion sin separador
print(table.concat(frutas))
-- Salida: manzananaranjaplátanouva
-- Concatenacion con separador
print(table.concat(frutas, ", "))
-- Salida: manzana, naranja, platano, uva
-- Concatenacion de un rango especifico
print(table.concat(frutas, " - ", 2, 3))
-- Salida: naranja - platano
Caso práctico - construcción de rutas:
ruta = {"usuario", "documentos", "proyectos", "lua"}
-- En Windows
rutaWindows = "C:\\" .. table.concat(ruta, "\\")
print(rutaWindows)
-- Salida: C:\usuario\documentos\proyectos\lua
-- En Unix/Linux
rutaUnix = "/" .. table.concat(ruta, "/")
print(rutaUnix)
-- Salida: /usuario/documentos/proyectos/lua
Nota importante: table.concat es significativamente más eficiente que concatenar cadenas con el operador .. dentro de un bucle, especialmente cuando trabajas con muchos elementos.
Función table.insert
La función insert añade un nuevo elemento a una tabla, desplazando los elementos existentes si es necesario para hacer espacio. Es la forma recomendada de añadir elementos a arrays en Lua.
Signatura:
table.insert(lista [, posicion], valor)
Parámetros:
- lista: tabla en la que insertaremos el elemento.
- posicion (opcional): índice donde se insertará el elemento. Si no se especifica, el elemento se añade al final de la lista.
- valor: elemento a insertar en la tabla.
Ejemplo básico:
paises = {"Espana", "Francia", "Italia"}
-- Insertar al final (sin especificar posicion)
table.insert(paises, "Portugal")
print(table.concat(paises, ", "))
-- Salida: Espana, Francia, Italia, Portugal
-- Insertar en posicion especifica
table.insert(paises, 2, "Alemania")
print(table.concat(paises, ", "))
-- Salida: Espana, Alemania, Francia, Italia, Portugal
Caso práctico - cola de tareas:
tareas = {}
-- Anadir tareas al final de la cola
table.insert(tareas, {nombre = "Hacer backup", prioridad = "alta"})
table.insert(tareas, {nombre = "Actualizar sistema", prioridad = "media"})
table.insert(tareas, {nombre = "Limpiar archivos temporales", prioridad = "baja"})
-- Anadir tarea urgente al principio
table.insert(tareas, 1, {nombre = "Corregir error critico", prioridad = "urgente"})
-- Mostrar todas las tareas
for i, tarea in ipairs(tareas) do
print(i .. ". " .. tarea.nombre .. " (" .. tarea.prioridad .. ")")
end
-- Salida:
-- 1. Corregir error critico (urgente)
-- 2. Hacer backup (alta)
-- 3. Actualizar sistema (media)
-- 4. Limpiar archivos temporales (baja)
Comportamiento importante: cuando insertas un elemento en una posición intermedia, todos los elementos desde esa posición en adelante se desplazan una posición hacia la derecha. Esto puede ser costoso en tablas muy grandes.
Función table.remove
La función remove elimina un elemento de la tabla y devuelve su valor. Los elementos posteriores se desplazan para llenar el hueco dejado.
Signatura:
table.remove(lista [, posicion])
Parámetros:
- lista: tabla de la cual eliminaremos el elemento.
- posicion (opcional): índice del elemento a eliminar. Si no se especifica, se elimina el último elemento de la lista.
Retorna: el valor del elemento eliminado.
Ejemplo básico:
colores = {"rojo", "verde", "azul", "amarillo", "negro"}
-- Eliminar elemento en posicion especifica
colorEliminado = table.remove(colores, 3)
print("Color eliminado: " .. colorEliminado)
-- Salida: Color eliminado: azul
print("Colores restantes: " .. table.concat(colores, ", "))
-- Salida: Colores restantes: rojo, verde, amarillo, negro
-- Eliminar el ultimo elemento (sin especificar posicion)
ultimoColor = table.remove(colores)
print("Ultimo color eliminado: " .. ultimoColor)
-- Salida: Ultimo color eliminado: negro
print("Colores finales: " .. table.concat(colores, ", "))
-- Salida: Colores finales: rojo, verde, amarillo
Caso práctico - implementación de una pila (stack):
pila = {}
-- Funcion para apilar (push)
function apilar(elemento)
table.insert(pila, elemento)
end
-- Funcion para desapilar (pop)
function desapilar()
if #pila > 0 then
return table.remove(pila)
else
return nil
end
end
-- Usar la pila
apilar("primero")
apilar("segundo")
apilar("tercero")
print(desapilar()) -- Salida: tercero
print(desapilar()) -- Salida: segundo
print(desapilar()) -- Salida: primero
print(desapilar()) -- Salida: nil
Nota de rendimiento: eliminar elementos del final de la tabla es muy eficiente. Sin embargo, eliminar elementos del principio o del medio requiere desplazar todos los elementos posteriores, lo cual puede ser lento en tablas grandes.
Función table.move
La función move copia elementos de una tabla a otra (o a una posición diferente dentro de la misma tabla). Esta función está disponible a partir de Lua 5.3.
Signatura:
table.move(tablaOrigen, desde, hasta, posicionDestino [, tablaDestino])
Parámetros:
- tablaOrigen: tabla desde donde se copiarán los elementos.
- desde: índice del primer elemento a copiar.
- hasta: índice del último elemento a copiar (inclusive).
- posicionDestino: índice donde comenzará la inserción en la tabla destino.
- tablaDestino (opcional): tabla donde se insertarán los elementos copiados. Si se omite, los elementos se copian dentro de la misma
tablaOrigen.
Retorna: la tabla destino.
Ejemplo básico - copiar entre tablas:
origen = {"a", "b", "c", "d", "e"}
destino = {"x", "y", "z"}
-- Copiar elementos 2 al 4 de origen, a partir de posicion 2 de destino
table.move(origen, 2, 4, 2, destino)
print("Destino: " .. table.concat(destino, ", "))
-- Salida: Destino: x, b, c, d
print("Origen: " .. table.concat(origen, ", "))
-- Salida: Origen: a, b, c, d, e (sin cambios)
Ejemplo - mover dentro de la misma tabla:
lista = {10, 20, 30, 40, 50}
-- Mover elementos 1-3 a posicion 3 (dentro de la misma tabla)
table.move(lista, 1, 3, 3)
print(table.concat(lista, ", "))
-- Salida: 10, 20, 10, 20, 30
Caso práctico - rotación de elementos:
function rotarIzquierda(tabla)
if #tabla > 1 then
-- Guardar el primer elemento
local primero = tabla[1]
-- Mover todos los elementos una posicion hacia la izquierda
table.move(tabla, 2, #tabla, 1)
-- Colocar el primer elemento al final
tabla[#tabla] = primero
end
end
numeros = {1, 2, 3, 4, 5}
rotarIzquierda(numeros)
print(table.concat(numeros, ", "))
-- Salida: 2, 3, 4, 5, 1
Función table.sort
La función sort ordena los elementos de una tabla in situ (modifica la tabla original). Por defecto, ordena en orden ascendente usando el operador <.
Signatura:
table.sort(lista [, funcionComparacion])
Parámetros:
- lista: tabla a ordenar.
- funcionComparacion (opcional): función que recibe dos elementos y retorna
truesi el primer elemento debe aparecer antes que el segundo. Si se omite, se usa el operador<de Lua.
Ejemplo básico:
numeros = {45, 12, 78, 23, 56, 34}
-- Ordenacion ascendente (por defecto)
table.sort(numeros)
print(table.concat(numeros, ", "))
-- Salida: 12, 23, 34, 45, 56, 78
-- Ordenacion descendente con funcion personalizada
table.sort(numeros, function(a, b) return a > b end)
print(table.concat(numeros, ", "))
-- Salida: 78, 56, 45, 34, 23, 12
Ejemplo con cadenas:
nombres = {"Carlos", "Ana", "Beatriz", "Daniel"}
-- Orden alfabetico
table.sort(nombres)
print(table.concat(nombres, ", "))
-- Salida: Ana, Beatriz, Carlos, Daniel
-- Orden alfabetico inverso
table.sort(nombres, function(a, b) return a > b end)
print(table.concat(nombres, ", "))
-- Salida: Daniel, Carlos, Beatriz, Ana
Caso práctico - ordenar tabla de objetos:
estudiantes = {
{nombre = "Ana", nota = 85},
{nombre = "Carlos", nota = 92},
{nombre = "Beatriz", nota = 78},
{nombre = "Daniel", nota = 95}
}
-- Ordenar por nota (descendente)
table.sort(estudiantes, function(a, b)
return a.nota > b.nota
end)
print("Ranking de estudiantes:")
for i, estudiante in ipairs(estudiantes) do
print(i .. ". " .. estudiante.nombre .. ": " .. estudiante.nota)
end
-- Salida:
-- Ranking de estudiantes:
-- 1. Daniel: 95
-- 2. Carlos: 92
-- 3. Ana: 85
-- 4. Beatriz: 78
Ordenación por múltiples criterios:
productos = {
{nombre = "Laptop", categoria = "Electronica", precio = 800},
{nombre = "Raton", categoria = "Electronica", precio = 25},
{nombre = "Silla", categoria = "Muebles", precio = 150},
{nombre = "Escritorio", categoria = "Muebles", precio = 300}
}
-- Ordenar primero por categoria, luego por precio
table.sort(productos, function(a, b)
if a.categoria == b.categoria then
return a.precio < b.precio
else
return a.categoria < b.categoria
end
end)
for _, prod in ipairs(productos) do
print(prod.categoria .. " - " .. prod.nombre .. ": " .. prod.precio .. " euros")
end
-- Salida:
-- Electronica - Raton: 25 euros
-- Electronica - Laptop: 800 euros
-- Muebles - Silla: 150 euros
-- Muebles - Escritorio: 300 euros
Nota importante: table.sort modifica la tabla original. Si necesitas mantener el orden original, haz una copia antes de ordenar.
Función table.unpack
La función unpack extrae y retorna los elementos de una tabla como valores individuales. Es la operación inversa de table.pack.
Signatura:
table.unpack(lista [, inicio [, fin]])
Parámetros:
- lista: tabla de la cual extraer elementos.
- inicio (opcional): índice del primer elemento a extraer. Por defecto es 1.
- fin (opcional): índice del último elemento a extraer. Por defecto es
#lista.
Retorna: los elementos de la tabla como valores separados.
Ejemplo básico:
coordenadas = {10, 20, 30}
-- Extraer todos los elementos
x, y, z = table.unpack(coordenadas)
print("x: " .. x .. ", y: " .. y .. ", z: " .. z)
-- Salida: x: 10, y: 20, z: 30
-- Extraer elementos especificos
primera, segunda = table.unpack(coordenadas, 1, 2)
print("Primera: " .. primera .. ", Segunda: " .. segunda)
-- Salida: Primera: 10, Segunda: 20
Caso práctico - paso de argumentos:
function calcularAreaRectangulo(ancho, alto)
return ancho * alto
end
dimensiones = {15, 8}
-- Usar unpack para pasar los elementos como argumentos
area = calcularAreaRectangulo(table.unpack(dimensiones))
print("Area: " .. area)
-- Salida: Area: 120
Intercambio de variables:
valores = {5, 10}
-- Intercambiar usando unpack
valores[1], valores[2] = table.unpack(valores, 2, 1)
print(table.concat(valores, ", "))
-- Esto no funciona como esperamos. Mejor usar:
a, b = 5, 10
a, b = b, a -- Forma correcta de intercambiar
print(a, b)
-- Salida: 10, 5
Función table.pack
La función pack crea una tabla a partir de una lista de valores. Es útil cuando trabajas con funciones que retornan múltiples valores.
Signatura:
table.pack(...)
Parámetros:
- ...: número variable de argumentos a empaquetar.
Retorna: una tabla con todos los argumentos. La tabla también incluye un campo n con el número total de elementos (útil cuando algunos valores son nil).
Ejemplo básico:
function obtenerDatos()
return "Juan", 25, "Madrid"
end
-- Empaquetar valores retornados en una tabla
datos = table.pack(obtenerDatos())
print("Elementos en la tabla: " .. datos.n)
print("Nombre: " .. datos[1])
print("Edad: " .. datos[2])
print("Ciudad: " .. datos[3])
-- Salida:
-- Elementos en la tabla: 3
-- Nombre: Juan
-- Edad: 25
-- Ciudad: Madrid
Caso práctico - función con argumentos variables:
function promediar(...)
local valores = table.pack(...)
local suma = 0
for i = 1, valores.n do
suma = suma + valores[i]
end
return suma / valores.n
end
print("Promedio: " .. promediar(10, 20, 30, 40, 50))
-- Salida: Promedio: 30.0
print("Promedio: " .. promediar(5, 15))
-- Salida: Promedio: 10.0
Manejo de valores nil:
-- table.pack preserva los nil
valores = table.pack(1, nil, 3, nil, 5)
print("Total de elementos: " .. valores.n)
-- Salida: Total de elementos: 5
-- Podemos iterar correctamente incluyendo los nil
for i = 1, valores.n do
print("Elemento " .. i .. ": " .. tostring(valores[i]))
end
-- Salida:
-- Elemento 1: 1
-- Elemento 2: nil
-- Elemento 3: 3
-- Elemento 4: nil
-- Elemento 5: 5
Combinando funciones de table
Las funciones de la librería table son más poderosas cuando las combinas. Aquí algunos ejemplos prácticos:
Filtrar y ordenar datos:
function filtrarMayoresQue(lista, valor)
local resultado = {}
for _, elemento in ipairs(lista) do
if elemento > valor then
table.insert(resultado, elemento)
end
end
table.sort(resultado)
return resultado
end
numeros = {45, 12, 78, 23, 56, 34, 89, 15}
mayoresQue30 = filtrarMayoresQue(numeros, 30)
print("Numeros mayores que 30: " .. table.concat(mayoresQue30, ", "))
-- Salida: Numeros mayores que 30: 34, 45, 56, 78, 89
Gestión de historial:
historial = {}
maximoHistorial = 10
function agregarAlHistorial(comando)
table.insert(historial, 1, comando) -- Insertar al principio
-- Mantener solo los ultimos 10 elementos
while #historial > maximoHistorial do
table.remove(historial) -- Eliminar el ultimo
end
end
-- Simular comandos
agregarAlHistorial("abrir archivo.txt")
agregarAlHistorial("buscar 'patron'")
agregarAlHistorial("guardar")
print("Historial reciente:")
for i, cmd in ipairs(historial) do
print(i .. ". " .. cmd)
end
-- Salida:
-- Historial reciente:
-- 1. guardar
-- 2. buscar 'patron'
-- 3. abrir archivo.txt
Resumen
En este artículo hemos explorado a fondo la librería table de Lua, una herramienta fundamental para trabajar eficientemente con arrays y listas. Aprendiste cómo table.concat te permite unir elementos de forma eficiente, table.insert y table.remove facilitan la modificación dinámica de listas, table.move permite copiar rangos de elementos, table.sort ordena tus datos con criterios personalizables, y table.pack/table.unpack convierten entre listas de valores y tablas.
Dominar estas funciones te permitirá escribir código más limpio, eficiente y profesional. Son las herramientas básicas que todo programador de Lua debe tener en su arsenal para manipular colecciones de datos de manera efectiva. En los próximos artículos exploraremos conceptos más avanzados como iteradores personalizados y técnicas de programación funcional con tablas.