Buscar..
Sintaxis
- ipairs (numeric_table) - tabla Lua con índices numéricos iterador
- pares (input_table) - iterador genérico de tabla Lua
- key, value = next (input_table, input_key) - Selector de valores de tabla Lua
- table.insert (input_table, [posición], valor): inserte el valor especificado en la tabla de entrada
- removed_value = table.remove (input_table, [position]): resalta el último o elimina el valor especificado por position
Observaciones
Las tablas son la única estructura de datos incorporada disponible en Lua. Esto puede ser elegante o confuso, dependiendo de cómo lo mires.
Una tabla Lua es una colección de pares clave-valor donde las claves son únicas y ni la clave ni el valor son nil
. Como tal, una tabla Lua puede parecerse a un diccionario, un hashmap o una matriz asociativa de otros idiomas. Muchos patrones estructurales se pueden construir con tablas: pilas, colas, conjuntos, listas, gráficos, etc. Finalmente, las tablas se pueden usar para construir clases en Lua y para crear un sistema de módulos .
Lua no impone ninguna regla particular sobre cómo se usan las tablas. Los elementos contenidos en una tabla pueden ser una mezcla de tipos de Lua. Así, por ejemplo, una tabla podría contener cadenas, funciones, valores booleanos, números e incluso otras tablas como valores o claves.
Se dice que una tabla Lua con teclas enteras positivas consecutivas que comienzan con 1 tiene una secuencia. Los pares clave-valor con claves enteras positivas son los elementos de la secuencia. Otros lenguajes llaman a esto una matriz basada en 1. Ciertas operaciones y funciones estándar solo funcionan en la secuencia de una tabla y algunas tienen un comportamiento no determinista cuando se aplican a una tabla sin una secuencia.
Establecer un valor en una tabla en nil
elimina de la tabla. Los iteradores ya no verían la clave relacionada. Al codificar una tabla con una secuencia, es importante evitar romper la secuencia; Solo elimine el último elemento o use una función, como la table.remove
estándar.eliminar, que desplaza los elementos hacia abajo para cerrar la brecha.
Creando tablas
Crear una tabla vacía es tan simple como esto:
local empty_table = {}
También puede crear una tabla en forma de una matriz simple:
local numeric_table = {
"Eve", "Jim", "Peter"
}
-- numeric_table[1] is automatically "Eve", numeric_table[2] is "Jim", etc.
Tenga en cuenta que, de forma predeterminada, la indexación de tablas comienza en 1.
También es posible crear una tabla con elementos asociativos:
local conf_table = {
hostname = "localhost",
port = 22,
flags = "-Wall -Wextra"
clients = { -- nested table
"Eve", "Jim", "Peter"
}
}
El uso anterior es sintaxis de azúcar para lo que está debajo. Las claves en este caso son del tipo cadena. La sintaxis anterior se agregó para que las tablas aparezcan como registros. Esta sintaxis de estilo de registro es paralela a la sintaxis para indexar tablas con claves de cadena, como se ve en el tutorial de "uso básico".
Como se explica en la sección de comentarios, la sintaxis de estilo de grabación no funciona para todas las claves posibles. Además, una clave puede ser cualquier valor de cualquier tipo, y los ejemplos anteriores solo cubrían cadenas y números secuenciales. En otros casos necesitarás usar la sintaxis explícita:
local unique_key = {}
local ops_table = {
[unique_key] = "I'm unique!"
["^"] = "power",
[true] = true
}
Mesas iterantes
La biblioteca estándar de Lua proporciona una función de pairs
que itera sobre las claves y los valores de una tabla. Cuando se itera con pairs
no hay un orden específico para el recorrido, incluso si las claves de la tabla son numéricas .
for key, value in pairs(input_table) do
print(key, " -- ", value)
end
Para tablas que usan teclas numéricas , Lua proporciona una función ipairs
. La función ipairs
siempre se repetirá desde la table[1]
, la table[2]
, etc. hasta que se encuentre el primer valor nil
.
for index, value in ipairs(numeric_table) do
print(index, ". ", value)
end
Tenga en cuenta que la iteración que usa ipairs()
no funcionará como desearía en algunas ocasiones:
input_table
tiene "agujeros" en él. (Para obtener más información, consulte la sección "Cómo evitar los huecos en las tablas utilizadas como matrices"). Por ejemplo:table_with_holes = {[1] = "value_1", [3] = "value_3"}
Las teclas no eran todas numéricas. Por ejemplo:
mixed_table = {[1] = "value_1", ["not_numeric_index"] = "value_2"}
Por supuesto, lo siguiente también funciona para una tabla que es una secuencia adecuada:
for i = 1, #numeric_table do
print(i, ". ", numeric_table[i])
end
Iterar una tabla numérica en orden inverso es fácil:
for i = #numeric_table, 1, -1 do
print(i, ". ", numeric_table[i])
end
Una forma final de iterar sobre tablas es usar el next
selector en un bucle genérico for
. Al igual que los pairs
no hay un orden especificado para el recorrido. (El método de pairs
usa next
internamente. Por lo tanto, usar next
es esencialmente una versión más manual de pairs
. Consulte los pairs
en el manual de referencia de Lua y next
en el manual de referencia de Lua para obtener más detalles).
for key, value in next, input_table do
print(key, value)
end
Uso básico
El uso básico de la tabla incluye acceder y asignar elementos de la tabla, agregar contenido de la tabla y eliminar el contenido de la tabla. Estos ejemplos asumen que sabes cómo crear tablas.
Elementos de acceso
Dada la siguiente tabla,
local example_table = {"Nausea", "Heartburn", "Indigestion", "Upset Stomach",
"Diarrhea", cure = "Pepto Bismol"}
Uno puede indexar la parte secuencial de la tabla utilizando la sintaxis del índice, siendo el argumento de la sintaxis del índice la clave del par clave-valor deseado. Como se explica en el tutorial de creación, la mayor parte de la sintaxis de la declaración es azúcar sintáctica para declarar pares clave-valor. Los elementos incluidos secuencialmente, como los primeros cinco valores en example_table
, utilizan valores enteros crecientes como claves; la sintaxis de registro utiliza el nombre del campo como una cadena.
print(example_table[2]) --> Heartburn
print(example_table["cure"]) --> Pepto Bismol
Para las claves de cadena, hay una sintaxis de azúcar para establecer un paralelo con la sintaxis de estilo de registro para las claves de cadena en la creación de tablas. Las siguientes dos líneas son equivalentes.
print(example_table.cure) --> Pepto Bismol
print(example_table["cure"]) --> Pepto Bismol
Puede acceder a las tablas utilizando las claves que no ha usado antes, eso no es un error como en otros idiomas. Al hacerlo, devuelve el valor predeterminado nil
.
Asignando elementos
Puede modificar los elementos de tabla existentes asignándolos a una tabla mediante la sintaxis de índice. Además, la sintaxis de indexación de estilo de registro también está disponible para establecer valores
example_table.cure = "Lots of water, the toilet, and time"
print(example_table.cure) --> Lots of water, the toilet, and time
example_table[2] = "Constipation"
print(example_table[2]) --> Constipation
También puede agregar nuevos elementos a una tabla existente usando la asignación.
example_table.copyright_holder = "Procter & Gamble"
example_table[100] = "Emergency source of water"
Observación especial: algunas cadenas no son compatibles con la sintaxis de registro. Vea la sección de comentarios para más detalles.
Removiendo elementos
Como se indicó anteriormente, el valor predeterminado para una clave sin valor asignado es nil
. Eliminar un elemento de una tabla es tan simple como restablecer el valor de una clave al valor predeterminado.
example_table[100] = "Face Mask"
Los elementos ahora son indistinguibles de un elemento no establecido.
Longitud de la mesa
Las tablas son simplemente matrices asociativas (ver comentarios), pero cuando se usan claves enteras contiguas comenzando desde 1, se dice que la tabla tiene una secuencia .
La búsqueda de la longitud de la parte de la secuencia de una tabla se realiza utilizando #
:
local example_table = {'a', 'l', 'p', 'h', 'a', 'b', 'e', 't'}
print(#example_table) --> 8
Puede usar la operación de longitud para agregar fácilmente elementos a una tabla de secuencia.
example_table[#example_table+1] = 'a'
print(#example_table) --> 9
En el ejemplo anterior, el valor anterior de #example_table
es 8
, al agregar 1
obtiene la siguiente clave de entero válida en la secuencia, 9
, así que ... example_table[9] = 'a'
. Esto funciona para cualquier longitud de tabla.
Observación especial: el uso de teclas de enteros que no son contiguas y que comienzan desde 1 rompe la secuencia de la tabla en una tabla dispersa . El resultado de la operación de longitud no está definido en ese caso. Vea la sección de comentarios.
Uso de las funciones de la biblioteca de tablas para agregar / eliminar elementos
Otra forma de agregar elementos a una tabla es la función table.insert()
. La función de inserción solo funciona en tablas de secuencia. Hay dos formas de llamar a la función. El primer ejemplo muestra el primer uso, donde se especifica el índice para insertar el elemento (el segundo argumento). Esto empuja todos los elementos del índice dado a #table
una posición. El segundo ejemplo muestra el otro uso de table.insert()
, donde el índice no se especifica y el valor dado se agrega al final de la tabla (índice #table + 1
).
local t = {"a", "b", "d", "e"}
table.insert(t, 3, "c") --> t = {"a", "b", "c", "d", "e"}
t = {"a", "b", "c", "d"}
table.insert(t, "e") --> t = {"a", "b", "c", "d", "e"}
En paralelo a table.insert()
para eliminar elementos está table.remove()
. De manera similar, tiene dos semánticas de llamada: una para eliminar elementos en una posición dada y otra para eliminar desde el final de la secuencia. Al eliminar desde la mitad de una secuencia, todos los elementos siguientes se desplazan hacia abajo un índice.
local t = {"a", "b", "c", "d", "e"}
local r = table.remove(t, 3) --> t = {"a", "b", "d", "e"}, r = "c"
t = {"a", "b", "c", "d", "e"}
r = table.remove(t) --> t = {"a", "b", "c", "d"}, r = "e"
Estas dos funciones mutan la tabla dada. Como podría indicar el segundo método para llamar a table.insert()
y table.remove()
proporciona la semántica de la pila a las tablas. Aprovechando eso, puede escribir código como el siguiente ejemplo.
function shuffle(t)
for i = 0, #t-1 do
table.insert(t, table.remove(t, math.random(#t-i)))
end
end
Implementa el Shuffle de Fisher-Yates, quizás de manera ineficiente. Utiliza table.insert()
para agregar el elemento extraído aleatoriamente al final de la misma tabla, y table.remove()
para extraer aleatoriamente un elemento de la parte restante de la tabla sin modificar.
Evitar los huecos en las tablas utilizadas como matrices.
Definiendo nuestros términos
Por matriz aquí nos referimos a una tabla Lua utilizada como secuencia. Por ejemplo:
-- Create a table to store the types of pets we like.
local pets = {"dogs", "cats", "birds"}
Estamos utilizando esta tabla como una secuencia: un grupo de elementos codificados por enteros. Muchos idiomas llaman a esto una matriz, y nosotros también. Pero estrictamente hablando, no hay tal cosa como una matriz en Lua. Solo hay tablas, algunas de las cuales son de tipo matriz, algunas de las cuales son de tipo hash (o de tipo diccionario, si lo prefiere), y algunas de las cuales están mezcladas.
Un punto importante sobre nuestra variedad de pets
es que no tiene huecos. El primer artículo, pets[1]
, es la cadena "perros", el segundo artículo, pets[2]
, es la cadena "gatos", y el último artículo, pets[3]
, es "pájaros". La biblioteca estándar de Lua y la mayoría de los módulos escritos para Lua asumen que 1 es el primer índice de secuencias. Por lo tanto, una matriz sin 1..n
tiene elementos de 1..n
sin perder ningún número en la secuencia. (En el caso límite, n = 1
, y la matriz solo contiene un elemento).
Lua proporciona la función ipairs
para iterar sobre dichas tablas.
-- Iterate over our pet types.
for idx, pet in ipairs(pets) do
print("Item at position " .. idx .. " is " .. pet .. ".")
end
Esto imprimiría "El artículo en la posición 1 es perros", "El artículo en la posición 2 es gatos", "El artículo en la posición 3 es pájaros".
Pero, ¿qué pasa si hacemos lo siguiente?
local pets = {"dogs", "cats", "birds"}
pets[12] = "goldfish"
for idx, pet in ipairs(pets) do
print("Item at position " .. idx .. " is " .. pet .. ".")
end
Una matriz como este segundo ejemplo es una matriz dispersa. Hay lagunas en la secuencia. Esta matriz se ve así:
{"dogs", "cats", "birds", nil, nil, nil, nil, nil, nil, nil, nil, "goldfish"}
-- 1 2 3 4 5 6 7 8 9 10 11 12
Los valores nulos no ocupan memoria adicional; internamente lua solo guarda los valores [1] = "dogs"
, [2] = "cats"
, [3] = "birtds"
y [12] = "goldfish"
Para responder a la pregunta inmediata, ipairs
se detendrá después de las aves; "goldfish" en pets[12]
nunca se alcanzará a menos que ajustemos nuestro código. Esto se debe a que ipairs
itera desde 1..n-1
donde n
es la posición del primer nil
encontrado. Lua define la table[length-of-table + 1]
como nil
. Entonces, en una secuencia apropiada, la iteración se detiene cuando Lua intenta obtener, por ejemplo, el cuarto elemento en una matriz de tres elementos.
¿Cuando?
Los dos lugares más comunes para que surjan problemas con matrices dispersas son (i) cuando se trata de determinar la longitud de la matriz y (ii) cuando se intenta iterar sobre la matriz. En particular:
- Cuando se usa el operador de longitud
#
ya que el operador de longitud deja de contar en el primernil
encontrado. - Cuando se utiliza la función
ipairs()
ya que como se mencionó anteriormente, deja de iterar en el primernil
encontrado. - Cuando se utiliza la función
table.unpack()
ya que este método detiene el desempaquetado en el primernil
encontrado. - Cuando se utilizan otras funciones que (directa o indirectamente) acceden a cualquiera de las anteriores.
Para evitar este problema, es importante escribir su código de modo que si espera que una tabla sea una matriz, no introduzca espacios. Las brechas se pueden introducir de varias maneras:
- Si agrega algo a una matriz en la posición incorrecta.
- Si inserta un valor
nil
en una matriz. - Si eliminas valores de una matriz.
Podrías pensar: "Pero nunca haría ninguna de esas cosas". Bueno, no intencionalmente, pero aquí hay un ejemplo concreto de cómo las cosas pueden salir mal. Imagina que deseas escribir un método de filtro para Lua como Ruby select
y Perl's grep
. El método aceptará una función de prueba y una matriz. Se itera sobre la matriz, llamando al método de prueba en cada elemento a su vez. Si el elemento pasa, entonces ese elemento se agrega a una matriz de resultados que se devuelve al final del método. La siguiente es una implementación con errores:
local filter = function (fun, t)
local res = {}
for idx, item in ipairs(t) do
if fun(item) then
res[idx] = item
end
end
return res
end
El problema es que cuando la función devuelve false
, omitimos un número en la secuencia. Imagine el filter(isodd, {1,2,3,4,5,6,7,8,9,10})
llamadas filter(isodd, {1,2,3,4,5,6,7,8,9,10})
: habrá espacios en la tabla devuelta cada vez que haya un número par en la matriz que se pasa al filter
.
Aquí hay una implementación fija:
local filter = function (fun, t)
local res = {}
for _, item in ipairs(t) do
if fun(item) then
res[#res + 1] = item
end
end
return res
end
Consejos
- Use las funciones estándar:
table.insert(<table>, <value>)
siempre se agrega al final de la matriz.table[#table + 1] = value
es una mano corta para esto.table.remove(<table>, <index>)
moverá todos los valores siguientes para llenar el espacio (lo que también puede hacerlo lento). - Compruebe si hay valores
nil
antes de insertar, evitando cosas comotable.pack(function_call())
, que podría deslizar valoresnil
en nuestra tabla. - Verifique los valores
nil
después de la inserción y, si es necesario, llene el espacio desplazando todos los valores consecutivos. - Si es posible, utilice valores de marcador de posición. Por ejemplo, cambie
nil
por0
o algún otro valor de marcador de posición. - Si es inevitable dejar brechas, esto debe documentarse (comentar).
- Escriba un
__len()
y use el operador#
.
Ejemplo para 6 .:
tab = {"john", "sansa", "daenerys", [10] = "the imp"}
print(#tab) --> prints 3
setmetatable(tab, {__len = function() return 10 end})
-- __len needs to be a function, otherwise it could just be 10
print(#tab) --> prints 10
for i=1, #tab do print(i, tab[i]) end
--> prints:
-- 1 john
-- 2 sansa
-- 3 daenerys
-- 4 nil
-- ...
-- 10 the imp
for key, value in ipairs(tab) do print(key, value) end
--> this only prints '1 john \n 2 sansa \n 3 daenerys'
Otra alternativa es usar la función pairs()
y filtrar los índices no enteros:
for key in pairs(tab) do
if type(key) == "number" then
print(key, tab[key]
end
end
-- note: this does not remove float indices
-- does not iterate in order