Come ottenere il numero di voci in una tabella Lua?


132

Sembra una domanda "fammi andare su Google per te", ma in qualche modo non riesco a trovare una risposta. L' #operatore Lua conta solo le voci con chiavi intere e quindi 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"

Come posso ottenere il numero di tutte le voci senza contarle?


3
@lhf: ho scritto un serializzatore che ricorda ogni oggetto che ha visto, e la prossima volta che lo vede emette un riferimento intero anziché l'oggetto. Il modo naturale di scrivere questo è qualcosa di simile dictionary[value] = #dictionary + 1, dove #rappresenta il numero di tutti gli oggetti. Quello che mi chiedo è perché non vuoi questo: in tutti i casi di uso sano per # (vedi risposta di kaizer.se), il conteggio di tutti gli oggetti è esattamente uguale a ciò che # restituisce già; sembra che fare # contare tutto sia strettamente un miglioramento. Ovviamente sono un principiante di Lua e potrebbe non capire il punto.
Roman Starkov,

32
@lhf: Non è carino da parte tua mettere in discussione la competenza del programmatore chiedendogli perché deve fare qualcosa per cui tutti i linguaggi di programmazione ragionevoli hanno una funzione semplice.
Timwi,

5
@Timwi: Non è carino da parte tua dire a uno degli autori del linguaggio Lua che Lua non è tra i linguaggi di programmazione "ragionevoli". ;-) A proposito, non ho mai avuto bisogno di quelle informazioni.
Alexander Gladysh,

5
Non credo di aver mai usato tutte le funzionalità di una singola lingua. Ciò non significa che non siano utili agli altri :)
Roman Starkov,

7
@sylvanaar Secondo me, l' #operatore è semplicemente mal definito. Questo è così facilmente risolvibile: in primo luogo, rendere #deterministico, e in secondo luogo, introdurre un nuovo operatore o funzione per ottenere il conteggio maledetto. Fine della storia ... Perché devono essere così testardi? :)
Roman Starkov,

Risposte:


129

Hai già la soluzione nella domanda: l'unico modo è di iterare l'intera tabella pairs(..).

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

Si noti inoltre che la definizione dell'operatore "#" è un po 'più complicata di così. Permettetemi di illustrarlo prendendo questo tavolo:

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

Secondo il manuale, uno qualsiasi di 3, 5 e 9 sono risultati validi per #t. L'unico modo sano di usarlo è con array di una parte contigua senza valori nulli.


42
Rabbrividisco ancora alla memoria della mia esperienza con Lua, quando ho capito per la prima volta che il valore di ritorno di un operatore di base come #non è deterministico.
Roman Starkov,

5
Oh, è probabilmente deterministico. È esattamente la stessa cosa di quando lo standard C lascia un comportamento definito dall'implementazione. Il motivo è che diversi implementatori possono scegliere diverse opzioni di implementazione.
Nakedible,

19
According to the manual, any of 3, 5 and 9 are valid results for #t. Secondo il manuale, chiamare # su non sequenze non è definito . Ciò significa che qualsiasi risultato (-1, 3, 3.14, 5, 9) è valido.
cubuspl42,

6
Per quanto riguarda i risultati validi: u0b34a0f6ae è corretto per Lua 5.1, mentre cubuspl42 è corretto per Lua 5.2. In entrambi i casi, l'intera faccenda è completamente folle.
Jeremy,

9
Il fatto che # su una non-sequenza non generi un'eccezione è solo una delle cose che rende l'uso di lua un po 'come tagliare te stesso per sentirti meglio.
Boatcoder

21

È possibile impostare una meta-tabella per tenere traccia del numero di voci, ciò potrebbe essere più veloce dell'iterazione se queste informazioni sono necessarie frequentemente.


Esiste un modo conveniente per gestire la cancellazione di voci con questo metodo?
u0b34a0f6ae

Purtroppo, sembra che la funzione __newindex non si attivi su zero assegnazioni a meno che l'indice non esista, quindi sembra che dovresti incanalare la rimozione delle voci attraverso una funzione speciale.
ergosys,

1
È necessario archiviare i dati in una tabella separata (ad esempio accessibile come upvalue per __index e __newindex). Quindi __index e __newindex verrebbero attivati ​​per ogni accesso alla tabella. Tuttavia, è necessario verificare se le prestazioni sono accettabili.
Alexander Gladysh,

@Alexander: Ah sì, e poi il prossimo punto di inciampo: se si esegue il proxy della tabella, la normale iterazione per coppie non funziona. Questo sarà possibile risolvere in Lua 5.2, ho sentito.
u0b34a0f6ae,

Ci sarebbero metametodi __pairs e __ipairs in 5.2 ... Se vuoi farlo in 5.1, dovresti sostituire la funzione Pair () con la tua. Ma probabilmente è troppo. :-)
Alexander Gladysh,

3

C'è un modo, ma potrebbe essere deludente: utilizzare una variabile aggiuntiva (o uno del campo della tabella) per memorizzare il conteggio e aumentarlo ogni volta che si effettua un inserimento.

count = 0
tbl = {}

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

tbl[1] = 48
count = count + 1

print(count)   -- prints "2"

Non c'è altro modo, l'operatore # funziona solo su tabelle simili a array con chiavi consecutive.


3
Questo può essere automatizzato con una tabella proxy e metametodi, come menzionato dalla risposta di
ergosys

Ho avuto l'impressione dai commenti che la cosa proxytable / metamethods non supporti ancora completamente questo scenario, quindi accetterò questo come il miglior modo attualmente disponibile.
Roman Starkov,

Il conteggio è l'unico modo per le tabelle e l'aggiunta di una riga durante la creazione delle tabelle è meglio di una funzione per contarle ogni volta che è necessario il conteggio. È possibile aggiungere una chiave alla fine con il valore impostato sul conteggio.
Henrik Erlandsson,

2

Il modo più semplice che conosco per ottenere il numero di voci in una tabella è con '#'. #tableName ottiene il numero di voci purché siano numerate:

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

Purtroppo, se non sono numerati, non funzionerà.


2

È possibile utilizzare la libreria penlight . Ha una funzione sizeche fornisce la dimensione effettiva della tabella.

Ha implementato molte delle funzioni di cui potremmo avere bisogno durante la programmazione e la mancanza in Lua.

Ecco l'esempio per usarlo.

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

> #a
0

> tablex.size(a)
3

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)

1
Quando si pubblica una risposta, si consiglia di pubblicare la quantità minima di codice che risponde direttamente a una domanda e di spiegare come il codice risponde alla domanda. Vedi qui .
cst1992,

__newindexchiama solo quando viene definito un nuovo tasto, quindi non è possibile chiamare __newindexquando impostiamo nilun tasto esistente.
Frank AK,

-1

appare quando gli elementi della tabella vengono aggiunti con il metodo insert, getn restituirà correttamente. Altrimenti, dobbiamo contare tutti gli elementi

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

Stampa 2 correttamente

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.