Самый эффективный способ определить, пуста ли таблица Lua (не содержит записей)?

120

Какой наиболее эффективный способ определить, является ли таблица пустой (то есть в настоящее время не содержит значений в стиле массива или значений в стиле dict)?

В настоящее время я использую next():

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

Есть более эффективный способ?

Примечание. #Оператора здесь недостаточно, так как он работает только со значениями в стиле массива в таблице - поэтому #{test=2}он неотличим от того, #{}потому что оба возвращают 0. Также обратите внимание, что проверки, является ли переменная таблицы nilнедостаточной, поскольку я не ищу нулевые значения, а скорее таблицы с 0 записями (т.е. {}).

янтарный
источник

Ответы:

151

Ваш код эффективен, но неверен. (Учтите {[false]=0}.) Правильный код

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

Для максимальной эффективности вы захотите привязать nextк локальной переменной, например,

...
local next = next 
...
... if next(...) ...
Норман Рэмси
источник
1
Хорошая точка зрения на техническую корректность; в конкретных случаях, когда я использовал исходный код, falseэто не было ожидаемым ключом, поэтому он if notработал нормально, но я, вероятно, сделаю привычку сравнивать его nilв будущем, просто как хорошую привычку. И да, для скорости я привязал общие служебные функции к локальным варам. Тем не менее, спасибо за вклад.
Эмбер
1
Мне трудно согласиться с неправильностью, когда код работает так, как задумано
RD Alkire
4
Почему мы набираем скорость, делая local next?
Moberg
2
@Moberg Это связано с тем, как LUA обрабатывает свое пространство имен. Очень упрощенная версия - сначала он поднимается по локальным таблицам, поэтому, если local nextв текущем блоке есть a , он будет использовать его, затем перейдет к следующему блоку и повторите. Выйдя из локальных, он только тогда будет использовать глобальное пространство имен. Это его упрощенная версия, но, в конце концов, это определенно означает разницу в скорости работы программы.
ATaco
@Moberg - менее упрощенная версия, в контексте lua 5.2 и 5.3, заключается в том, что нелокальные переменные являются либо поисковыми запросами, либо поисковыми запросами _ENV. Upval должен пройти дополнительный уровень косвенного обращения, тогда как поиск _ENV - это поиск по таблице. В то время как местный
житель
1

Одна из возможностей - подсчитать количество элементов с помощью метатабельного ключа newindex. При назначении чего-то не nilувеличивайте счетчик (счетчик также может жить в метатаблице), а при назначении nilуменьшайте счетчик.

Тестирование на пустую таблицу будет заключаться в проверке счетчика с 0.

Вот указатель на метатабильную документацию

Однако мне нравится ваше решение, и я, честно говоря, не могу предположить, что мое решение в целом быстрее.

0x6adb015
источник
5
Первоначальный вопрос заключается не в подсчете только записей «массива».
lhf
3
Предложение 0x6 не относится к записям в стиле массива (newindex работает как для числовых, так и для нечисловых индексов). Однако основной проблемой будет определение того, когда nilон назначен, поскольку __newindex не запускается, если ключ уже существует в таблице.
Эмбер,
3
Чтобы этот трюк сработал, метатаблица должна реализовывать оба __indexи __newindex, сохраняя фактические данные в теневой таблице и оставляя реальную таблицу пустой, чтобы __indexона вообще вызывалась. Размышляя вслух, я подозреваю, что повышенная стоимость каждого поиска не окупается.
RBerteig
0

Вероятно, это то, что вы хотели:

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

Вывод:

true
false
true
FichteFoll
источник
11
next()более эффективен (и краток), чем цикл pairs().
Эмбер
8
Фактически, зацикливание pairs() - это, по сути, просто использование этой next()техники, но с большими накладными расходами.
dubiousjim 01
7
Также tableне рекомендуется писать в стандартную библиотеку.
Ti Strga
-1

лучше избегать оценки __eq при перегрузке.

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

или

if type(next(myTable)) == "nil" then
   -- myTable is empty
end
Лоран Дениау
источник
1
Я новичок в Lua, пытаясь понять, почему этот ответ был отклонен. Я предполагаю, что это потому, что в Lua «если два объекта имеют разные метаметоды, операция равенства приводит к ложному результату, даже без вызова какого-либо метаметода». (Цитата находится внизу этой страницы из раздела «Программирование на Lua» на lua.org ). Устраняет ли это необходимость избегать перегрузки __eq для nil?
SansWit
-1

попробуй змея, работай на меня

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
источник
-2

Как насчет этого ?

if endmyTable[1] == nil then
  -- myTable is empty
end
Венкат Редди
источник
1
Это не сработает для таблицы, которая
выглядит
-3

Я знаю, что это старое, и я могу почему-то вас неправильно понять, но если вы просто хотите, чтобы таблица была пустой, то есть, если вы просто не проверяете, есть ли она, и вы на самом деле не хотите или не хотите, чтобы она была пустой, вы можете очистить его, просто воссоздав его, если я не ошибаюсь. это можно сделать с помощью синтаксиса ниже.

yourtablename = {} -- this seems to work for me when I need to clear a table.
Майкл Рис
источник
4
Не в этом вопрос.
Ю Хао
-6

Попробуйте использовать #. Он возвращает все экземпляры, находящиеся в таблице. Если в таблице нет экземпляров, возвращается0

if #myTable==0 then
print('There is no instance in this table')
end
arthurgps2
источник
1
Спрашивающий говорит, что #этого здесь недостаточно, и объясняет, почему; не могли бы вы объяснить, почему это обходится без этих причин?
ameed
ну ... я не знаю. Я новичок в этом, поэтому единственный известный мне способ использовать #
arthurgps2