Il modo più efficiente per determinare se una tabella Lua è vuota (non contiene voci)?


120

Qual è il modo più efficiente per determinare se una tabella è vuota (ovvero, attualmente non contiene né valori in stile array né valori in stile dict)?

Attualmente sto usando next():

if not next(myTable) then
    -- Table is empty
end

C'è un modo più efficiente?

Nota: l' #operatore non è sufficiente qui, poiché opera solo sui valori in stile array nella tabella - quindi #{test=2}è indistinguibile da #{}perché entrambi restituiscono 0. Si noti inoltre che controllare se la variabile della tabella è nilnon è sufficiente perché non sto cercando valori nulli, ma piuttosto tabelle con 0 voci (cioè {}).

Risposte:


151

Il tuo codice è efficiente ma sbagliato. (Considera {[false]=0}.) Il codice corretto è

if next(myTable) == nil then
   -- myTable is empty
end

Per la massima efficienza ti consigliamo di collegarti nexta una variabile locale, ad esempio,

...
local next = next 
...
... if next(...) ...

1
Buon punto sulla correttezza tecnica; nei casi particolari in cui ho utilizzato il codice originale, falsenon sarebbe una chiave prevista quindi ha if notfunzionato bene, ma probabilmente prenderò l'abitudine di confrontarlo con nilinvece in futuro, proprio come una buona abitudine. E sì, ho associato le funzioni di utilità comuni alle variabili locali per la velocità. Grazie per il contributo però.
Amber

1
Trovo difficile essere d'accordo con l'erroneità quando il codice funziona come previsto
RD Alkire

4
Perché guadagniamo velocità facendo local next?
Moberg

2
@Moberg Ciò è dovuto al modo in cui LUA gestisce il proprio spazio dei nomi. La versione molto stupida è che salirà prima sui tavoli locali, quindi se c'è un local nextnel blocco corrente, lo userà, quindi salirà al blocco successivo e ripeterà. Una volta fuori dai locali, utilizzerà solo lo spazio dei nomi globale. Questa è una versione stupida, ma alla fine, fa sicuramente la differenza per quanto riguarda la velocità del programma.
ATaco

@Moberg la versione meno stupida, nel contesto di lua 5.2 e 5.3, è che i non locali sono o upvals o _ENV lookup. Un upval deve passare attraverso un ulteriore livello di riferimento indiretto, mentre una ricerca _ENV è una ricerca in una tabella. Considerando che un locale è un registro nel VM
Demur Rumed

1

Una possibilità sarebbe contare il numero di elementi, utilizzando la chiave metatable "newindex". Quando si assegna qualcosa di no nil, incrementare il contatore (il contatore potrebbe anche vivere nel metatabile) e durante l'assegnazione nil, diminuire il contatore.

Testare una tabella vuota significherebbe testare il contatore con 0.

Ecco un puntatore alla documentazione metatabile

Tuttavia, mi piace la tua soluzione e onestamente non posso presumere che la mia soluzione sia più veloce nel complesso.


5
La domanda originale non riguarda solo il conteggio delle voci "array".
lhf

3
Il suggerimento di 0x6 non è specifico per le voci in stile array (newindex funziona sia per gli indici numerici che per quelli non numerici). Tuttavia, il problema principale sarebbe rilevare quando nilviene assegnato, poiché __newindex non si attiva se la chiave esiste già nella tabella.
Amber

3
Affinché questo trucco funzioni, il metatable dovrebbe implementare sia __indexe __newindex, archiviare i dati effettivi in ​​una tabella shadow e mantenere vuota la tabella reale in modo che __indexvenga invocata. Pensando ad alta voce, sospetto che l'aumento del costo di ogni singola ricerca non possa valere la pena.
RBerteig

0

Questo è probabilmente quello che volevi:

function table.empty (self)
    for _, _ in pairs(self) do
        return false
    end
    return true
end

a = { }
print(table.empty(a))
a["hi"] = 2
print(table.empty(a))
a["hi"] = nil
print(table.empty(a))

Produzione:

true
false
true

11
next()è più efficiente (e più conciso) del loop over pairs().
Amber

8
Infatti, annodare sopra pairs() è essenzialmente solo utilizzando la next()tecnica, ma con maggiore overhead.
dubiousjim

7
Inoltre, tablenon è consigliabile scrivere nella libreria standard .
Ti Strga

-1

meglio evitare la valutazione di __eq se sovraccaricato.

if rawequal(next(myTable), nil) then
   -- myTable is empty
end

o

if type(next(myTable)) == "nil" then
   -- myTable is empty
end

1
Sono un noob Lua che cerca di capire perché questa risposta è stata votata. Immagino sia perché in Lua, "se due oggetti hanno metametodi diversi, l'operazione di uguaglianza risulta falsa, senza nemmeno chiamare alcun metametodo". (La citazione è in fondo a questa pagina da Programming in Lua su lua.org ). Ciò elimina la necessità di evitare il sovraccarico di __eq per zero?
SansWit

-1

prova serpente, lavora per me

serpent = require 'serpent'

function vtext(value)
  return serpent.block(value, {comment=false})
end

myTable = {}

if type(myTable) == 'table' and vtext(myTable) == '{}' then
   -- myTable is empty
end

-2

Cosa ne pensi di questo ?

if endmyTable[1] == nil then
  -- myTable is empty
end

1
Questo non funzionerà su una tabella che come stringhe come indice
SamHoque

-3

So che questo è vecchio e potrei fraintenderti in qualche modo, ma se vuoi solo che il tavolo sia vuoto, cioè, a meno che tu non stia solo controllando se lo è e non vuoi o non hai bisogno che sia vuoto, puoi cancellarlo semplicemente ricreandolo, a meno che non mi sbagli. questo può essere fatto con la sintassi seguente.

yourtablename = {} -- this seems to work for me when I need to clear a table.

4
Non è questa la domanda.
Yu Hao

-6

Prova a usare #. Restituisce tutte le istanze che si trovano in una tabella. Se non ci sono istanze in una tabella, restituisce0

if #myTable==0 then
print('There is no instance in this table')
end

1
Il richiedente dice che #qui non sarà sufficiente e spiega perché; potresti spiegare perché questo aggira questi motivi?
ameed

beh ... non lo so Sono nuovo in questo quindi l'unico modo che conosco è usare #
arthurgps2
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.