¿Cómo obtener el número de entradas en una tabla Lua?

132

Suena como una pregunta de "déjame buscarlo en Google", pero de alguna manera no puedo encontrar una respuesta. El #operador Lua solo cuenta las entradas con teclas enteras, y también lo hace table.getn:

tbl = {}
tbl["test"] = 47
tbl[1] = 48
print(#tbl, table.getn(tbl))   -- prints "1     1"

count = 0
for _ in pairs(tbl) do count = count + 1 end
print(count)            -- prints "2"

¿Cómo obtengo el número de todas las entradas sin contarlas?

Roman Starkov
fuente
3
@lhf: he escrito un serializador que recuerda cada objeto que ha visto, y la próxima vez que lo ve emite una referencia entera en lugar del objeto. La forma natural de escribir esto es algo así como dictionary[value] = #dictionary + 1, donde #representa el número de todos los objetos. Lo que me pregunto es por qué usted no desea que esto: en todos los cuerdo casos de uso de # (véase la respuesta por kaizer.se), el recuento de todos los objetos es exactamente igual a lo que ya # devuelve; parece que hacer que # cuente todo es estrictamente una mejora. Por supuesto que soy un novato de Lua y podría estar perdiendo el punto.
Roman Starkov
32
@lhf: No es amable de su parte cuestionar la competencia del programador preguntándole por qué necesita hacer algo para lo que todos los lenguajes de programación razonables tienen una función simple.
Timwi
55
@Timwi: No es amable de su parte decirle a uno de los autores del lenguaje Lua que Lua no se encuentra entre los lenguajes de programación "razonables". ;-) Por cierto, nunca necesité esa información también.
Alexander Gladysh
55
No creo haber usado todas las características de un solo idioma. Eso no significa que no sean útiles para otros :)
Roman Starkov
77
@sylvanaar En mi opinión, el #operador está mal definido. Esto es fácilmente reparable: en primer lugar, hacer #determinista y, en segundo lugar, introducir un nuevo operador o función para obtener el maldito recuento. Fin de la historia ... ¿Por qué tienen que ser tan tercos? :)
Roman Starkov

Respuestas:

129

Ya tiene la solución en la pregunta: la única forma es iterar toda la tabla con pairs(..).

function tablelength(T)
  local count = 0
  for _ in pairs(T) do count = count + 1 end
  return count
end

Además, observe que la definición del operador "#" es un poco más complicada que eso. Permítanme ilustrar eso tomando esta tabla:

t = {1,2,3}
t[5] = 1
t[9] = 1

Según el manual, cualquiera de los 3, 5 y 9 son resultados válidos para #t. La única forma sensata de usarlo es con matrices de una parte contigua sin valores nulos.

u0b34a0f6ae
fuente
42
Todavía me estremezco al recordar mi experiencia con Lua, cuando me di cuenta por primera vez de que el valor de retorno de un operador básico #no es determinista.
Roman Starkov
55
Oh, es probable que sea determinista. Es exactamente lo mismo que cuando el estándar C deja algo para ser un comportamiento definido de implementación. La razón es que diferentes implementadores pueden elegir diferentes opciones de implementación.
Nakedible
19
According to the manual, any of 3, 5 and 9 are valid results for #t. Según el manual, llamar a # en no secuencias es no definido . Eso significa que cualquier resultado (-1, 3, 3.14, 5, 9) es válido.
cubuspl42
66
En cuanto a resultados válidos: u0b34a0f6ae es correcto para Lua 5.1, mientras que cubuspl42 es correcto para Lua 5.2. En cualquier caso, todo el asunto es completamente loco.
Jeremy
9
El hecho de que # en una no secuencia no genere una excepción es solo una de las cosas que hace que usar lua sea un poco como cortarse para sentirse mejor.
Boatcoder
21

Puede configurar una metatabla para rastrear el número de entradas, esto puede ser más rápido que la iteración si esta información es necesaria con frecuencia.

ergosys
fuente
¿Hay alguna manera conveniente de manejar el borrado de entradas con este método?
u0b34a0f6ae
Lamentablemente, parece que la función __newindex no se activa en asignaciones nulas a menos que el índice no exista, por lo que parece que tendría que canalizar la eliminación de entradas a través de una función especial.
ergosys
1
Debería almacenar los datos en una tabla separada (por ejemplo, accesible como valor ascendente para __index y __newindex). Entonces, tanto __index como __newindex se dispararían por cada acceso a la tabla. Sin embargo, debe verificar si el rendimiento es aceptable.
Alexander Gladysh
@Alexander: Ah, sí, y luego el siguiente punto de tropiezo: si representa la tabla, la iteración normal por pares no funciona. Esto será posible de resolver en Lua 5.2, escuché.
u0b34a0f6ae
Habría __pairs y __ipairs metamethods en 5.2 ... Si desea hacerlo en 5.1, tendría que reemplazar la función pares () por la suya. Pero eso es probablemente demasiado. :-)
Alexander Gladysh
3

Hay una manera, pero puede ser decepcionante: use una variable adicional (o uno de los campos de la tabla) para almacenar el recuento y aumente cada vez que realice una inserción.

count = 0
tbl = {}

tbl["test"] = 47
count = count + 1

tbl[1] = 48
count = count + 1

print(count)   -- prints "2"

No hay otra manera, el operador # solo funcionará en tablas tipo matriz con claves consecutivas.

kikito
fuente
3
Esto se puede automatizar con una tabla proxy y metametodos, como se menciona en la respuesta de
ergosys
Los comentarios me dieron la impresión de que lo de proxytable / metamethods aún no es totalmente compatible con este escenario, por lo que lo aceptaré como la mejor manera disponible actualmente.
Roman Starkov
El recuento es la única forma para las tablas, y agregar líneas cuando se crean las tablas es mejor que una función para contarlas cada vez que las necesita. Puede agregar una clave al final con el valor establecido en el recuento.
Henrik Erlandsson
2

La forma más fácil que conozco para obtener el número de entradas en una tabla es con '#'. #tableName obtiene el número de entradas siempre que estén numeradas:

tbl={
    [1]
    [2]
    [3]
    [4]
    [5]
}
print(#tbl)--prints the highest number in the table: 5

Lamentablemente, si no están numerados, no funcionará.

Oleada12
fuente
2

Podrías usar la biblioteca penlight . Esto tiene una función sizeque da el tamaño real de la tabla.

Ha implementado muchas de las funciones que podemos necesitar al programar y faltar en Lua.

Aquí está la muestra para usarlo.

> tablex = require "pl.tablex"
> a = {}
> a[2] = 2
> a[3] = 3 
> a['blah'] = 24

> #a
0

> tablex.size(a)
3

fuente
1
local function CountedTable(x)
    assert(type(x) == 'table', 'bad parameter #1: must be table')

    local new_t = {}
    local mt = {}

    -- `all` will represent the number of both
    local all = 0
    for k, v in pairs(x) do
        all = all + 1
    end

    mt.__newindex = function(t, k, v)
        if v == nil then
            if rawget(x, k) ~= nil then
                all = all - 1
            end
        else
            if rawget(x, k) == nil then
                all = all + 1
            end
        end

        rawset(x, k, v)
    end

    mt.__index = function(t, k)
        if k == 'totalCount' then return all
        else return rawget(x, k) end
    end

    return setmetatable(new_t, mt)
end

local bar = CountedTable { x = 23, y = 43, z = 334, [true] = true }

assert(bar.totalCount == 4)
assert(bar.x == 23)
bar.x = nil
assert(bar.totalCount == 3)
bar.x = nil
assert(bar.totalCount == 3)
bar.x = 24
bar.x = 25
assert(bar.x == 25)
assert(bar.totalCount == 4)
Sleepwom
fuente
1
Al publicar una respuesta, se recomienda publicar la cantidad mínima de código que responde directamente a una pregunta y explicar cómo el código responde a la pregunta. Vea aquí .
cst1992
__newindexsolo llama cuando se define una nueva clave, por lo que no hay posibilidad de llamar __newindexcuando establecemos niluna clave existente.
Frank AK
-1

parece que cuando los elementos de la tabla se agregan mediante el método de inserción, getn volverá correctamente. De lo contrario, tenemos que contar todos los elementos

mytable = {}
element1 = {version = 1.1}
element2 = {version = 1.2}
table.insert(mytable, element1)
table.insert(mytable, element2)
print(table.getn(mytable))

Imprimirá 2 correctamente

Yongxin Zhang
fuente