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 primer nil encontrado.
  • Cuando se utiliza la función ipairs() ya que como se mencionó anteriormente, deja de iterar en el primer nil encontrado.
  • Cuando se utiliza la función table.unpack() ya que este método detiene el desempaquetado en el primer nil 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

  1. 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).
  2. Compruebe si hay valores nil antes de insertar, evitando cosas como table.pack(function_call()) , que podría deslizar valores nil en nuestra tabla.
  3. Verifique los valores nil después de la inserción y, si es necesario, llene el espacio desplazando todos los valores consecutivos.
  4. Si es posible, utilice valores de marcador de posición. Por ejemplo, cambie nil por 0 o algún otro valor de marcador de posición.
  5. Si es inevitable dejar brechas, esto debe documentarse (comentar).
  6. 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


Modified text is an extract of the original Stack Overflow Documentation
Licenciado bajo CC BY-SA 3.0
No afiliado a Stack Overflow