Maneira mais eficiente de determinar se uma tabela Lua está vazia (não contém entradas)?

120

Qual é a maneira mais eficiente de determinar se uma tabela está vazia (ou seja, atualmente não contém valores de estilo de matriz nem valores de estilo de dict)?

Atualmente, estou usando next():

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

Existe uma maneira mais eficiente?

Nota: O #operador não é suficiente aqui, pois ele opera apenas nos valores de estilo de matriz na tabela - portanto, #{test=2}é indistinguível #{}porque ambos retornam 0. Observe também que verificar se a variável da tabela é nilnão é suficiente, pois não estou procurando valores nulos, mas sim tabelas com 0 entradas (ou seja {}).

Âmbar
fonte

Respostas:

151

Seu código é eficiente, mas errado. (Considere {[false]=0}.) O código correto é

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

Para máxima eficiência, você deseja vincular nexta uma variável local, por exemplo,

...
local next = next 
...
... if next(...) ...
Norman Ramsey
fonte
1
Bom ponto sobre a correção técnica; nos casos específicos em que tenho utilizado o código original, falsenão seria uma chave esperada, então if notfuncionou bem, mas provavelmente terei o hábito de comparar nilno futuro, apenas como um bom hábito. E sim, tenho vinculado funções de utilitário comuns a vars locais para aumentar a velocidade. Obrigado pela contribuição.
Amber de
1
Acho difícil concordar com o que está errado quando o código funciona conforme o
esperado
4
Por que ganhamos velocidade fazendo local next?
Moberg
2
@Moberg Isso se deve ao modo como LUA trata seu namespace. A versão mais simplificada é que primeiro subirá nas tabelas locais, então se houver um local nextno bloco atual, ele o usará, então subirá para o próximo bloco e repetirá. Uma vez fora dos locais, ele só usará o Global Namespace. Esta é uma versão simplificada dele, mas no final, definitivamente significa a diferença em relação à velocidade do programa.
ATaco
@Moberg a versão menos simplificada, no contexto de lua 5.2 e 5.3, é que os não locais são upvals ou pesquisas _ENV. Um upval tem que passar por uma camada extra de indireção, enquanto uma pesquisa _ENV é uma pesquisa de tabela. Considerando que um local é um registro no VM
Demur Rumed
1

Uma possibilidade seria contar o número de elementos, usando a chave "newindex" da metatabela. Ao atribuir algo diferente nil, aumente o contador (o contador também poderia residir na metatabela) e, ao atribuir nil, diminua o contador.

O teste de mesa vazia seria testar o contador com 0.

Aqui está um indicador para a documentação da metatabela

Eu gosto da sua solução e, honestamente, não posso presumir que minha solução seja mais rápida no geral.

0x6adb015
fonte
5
A questão original não é contar apenas entradas de "array".
lhf
3
A sugestão de 0x6 não é específica para entradas de estilo de matriz (newindex funciona para índices numéricos e não numéricos). No entanto, o principal problema seria detectar quando nilé atribuído, uma vez que __newindex não dispara se a chave já existe na tabela.
Amber
3
Para que esse truque funcione, a metatabela teria que implementar __indexe __newindex, armazenando os dados reais em uma tabela sombra e mantendo a tabela real vazia para que __indexseja invocada. Pensando em voz alta, suspeito que o custo elevado de cada consulta não pode valer a pena.
RBerteig de
0

Provavelmente é isso que você queria:

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))

Resultado:

true
false
true
FichteFoll
fonte
11
next()é mais eficiente (e mais conciso) do que repetir pairs().
Amber
8
Na verdade, o loop pairs() é essencialmente apenas usando a next()técnica, mas com mais overhead.
duvidosojim
7
Além disso, escrever na tablebiblioteca padrão não é recomendado.
Ti Strga de
-1

melhor evitar a avaliação de __eq se estiver sobrecarregado.

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

ou

if type(next(myTable)) == "nil" then
   -- myTable is empty
end
Laurent Deniau
fonte
1
Sou um novato da Lua tentando entender por que essa resposta foi rejeitada. Suponho que seja porque em Lua, "se dois objetos têm metamétodos diferentes, a operação de igualdade resulta em falso, sem nem mesmo chamar nenhum metamétodo". (A citação está no final desta página, extraída de Programming in Lua em lua.org ). Isso elimina a necessidade de evitar sobrecarga de __eq para zero?
SansWit
-1

tente serpente, trabalhe para mim

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
Webrom
fonte
-2

Que tal agora ?

if endmyTable[1] == nil then
  -- myTable is empty
end
Venkat Reddy
fonte
1
Isso não funcionará em uma tabela com strings como índice
SamHoque
-3

Eu sei que isso é antigo, e posso estar te entendendo mal de alguma forma, mas você só quer que a mesa esteja vazia, isto é, a menos que você esteja apenas verificando se ela está e você realmente não quer ou precisa que ela esteja vazia, você pode limpá-lo simplesmente recriando-o, a menos que eu esteja enganado. isso pode ser feito com a sintaxe abaixo.

yourtablename = {} -- this seems to work for me when I need to clear a table.
Michael Reece
fonte
4
Essa não é a questão.
Yu Hao
-6

Tente usar #. Ele retorna todas as instâncias que estão em uma tabela. Se não houver instâncias em uma tabela, ele retorna0

if #myTable==0 then
print('There is no instance in this table')
end
arthurgps2
fonte
1
O autor da pergunta diz que #isso não é suficiente aqui e dá as razões; você poderia explicar por que isso contorna esses motivos?
ameed
bem ... não sei. Sou novo nisso, então a única maneira que conheço é usando #
arthurgps2