Najbardziej efektywny sposób określenia, czy tabela Lua jest pusta (nie zawiera żadnych wpisów)?

120

Jaki jest najbardziej efektywny sposób określenia, czy tabela jest pusta (to znaczy, że obecnie nie zawiera wartości w stylu tablicowym ani w stylu dyktowania)?

Obecnie używam next():

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

Czy jest bardziej efektywny sposób?

Uwaga: #Operator nie jest tutaj wystarczający, ponieważ działa tylko na wartościach tablicowych w tabeli - dlatego #{test=2}jest nie do odróżnienia, #{}ponieważ oba zwracają 0. Zwróć również uwagę, że sprawdzenie, czy zmienna tabeli jest nilniewystarczające, ponieważ nie szukam wartości zerowe, ale raczej tabele z 0 wpisami (tj {}.).

Bursztyn
źródło

Odpowiedzi:

151

Twój kod jest skuteczny, ale zły. (Rozważ {[false]=0}.) Prawidłowy kod to

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

Aby uzyskać maksymalną wydajność, będziesz chciał powiązać nextsię ze zmienną lokalną, np.

...
local next = next 
...
... if next(...) ...
Norman Ramsey
źródło
1
Dobra uwaga na temat poprawności technicznej; w szczególnych przypadkach, gdy korzystałem z oryginalnego kodu, falsenie byłby to oczekiwany klucz, więc if notzadziałało dobrze, ale prawdopodobnie będę miał nawyk porównywania go nilw przyszłości, po prostu jako dobry nawyk. I tak, powiązałem typowe funkcje narzędziowe z lokalnymi zmiennymi w celu zwiększenia szybkości. W każdym razie dziękuję za wkład.
Amber
1
Trudno mi się zgodzić z błędem, gdy kod działa zgodnie z przeznaczeniem
RD Alkire
4
Dlaczego przyspieszamy, działając local next?
Moberg
2
@Moberg Wynika to ze sposobu, w jaki LUA obsługuje swoją przestrzeń nazw. Wersja bardzo wygłupiona polega na tym, że najpierw będzie wspinał się po lokalnych stołach, więc jeśli local nextw bieżącym bloku znajduje się element, użyje go, a następnie wejdzie do następnego bloku i powtórzy. Gdy wyjdzie z lokalnych, będzie używać tylko globalnej przestrzeni nazw. Jest to okrojona wersja tego, ale ostatecznie oznacza to zdecydowanie różnicę w szybkości działania programu.
ATaco
@Moberg, mniej wygłupiona wersja, w kontekście lua 5.2 i 5.3, jest taka, że ​​nielokalne są albo upvals, albo wyszukiwaniem _ENV. Upval musi przejść przez dodatkową warstwę pośrednią, podczas gdy wyszukiwanie _ENV jest wyszukiwaniem w tabeli. Podczas gdy lokalny jest rejestr w VM
Demur Rumed
1

Jedną z możliwości byłoby policzenie liczby elementów przy użyciu klucza metatable „newindex”. Kiedy przypisujesz coś nie nil, zwiększ licznik (licznik może również mieszkać w metatabeli), a podczas przypisywania nilzmniejsz licznik.

Testowanie pustej tabeli polegałoby na przetestowaniu licznika z 0.

Oto wskaźnik do metatowalnej dokumentacji

Jednak podoba mi się twoje rozwiązanie i szczerze mówiąc nie mogę założyć, że moje rozwiązanie jest ogólnie szybsze.

0x6adb015
źródło
5
Pierwotne pytanie nie dotyczy tylko liczenia wpisów „tablicowych”.
lhf
3
Sugestia 0x6 nie jest specyficzna dla wpisów w stylu tablicowym (newindex działa zarówno dla indeksów numerycznych, jak i nienumerycznych). Jednak głównym problemem byłoby wykrycie, kiedy niljest przypisany, ponieważ __newindex nie jest wyzwalany, jeśli klucz już istnieje w tabeli.
Bursztyn
3
Aby ta sztuczka zadziałała, metatablica musiałaby zaimplementować zarówno __indexi __newindex, jak i przechowywać rzeczywiste dane w tabeli-cieniu i utrzymywać rzeczywistą tabelę pustą, aby w __indexogóle została wywołana. Myśląc na głos, podejrzewam, że podniesiony koszt każdego wyszukiwania nie może być tego wart.
RBerteig
0

Prawdopodobnie tego chciałeś:

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

Wynik:

true
false
true
FichteFoll
źródło
11
next()jest bardziej wydajna (i bardziej zwięzła) niż pętla pairs().
Amber
8
W rzeczywistości zapętlenie pairs() to zasadniczo tylko użycie tej next()techniki, ale z większym narzutem.
wątpliwyjim
7
tableNie zaleca się również pisania do biblioteki standardowej .
Ti Strga
-1

lepiej unikać oceny __eq, jeśli jest przeciążony.

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

lub

if type(next(myTable)) == "nil" then
   -- myTable is empty
end
Laurent Deniau
źródło
1
Jestem noobem Lua, próbującym zrozumieć, dlaczego ta odpowiedź została odrzucona. Domyślam się, że dzieje się tak, ponieważ w Lua „jeśli dwa obiekty mają różne metametody, operacja równości skutkuje fałszem, nawet bez wywoływania żadnej metametody”. (Cytat znajduje się na dole strony z książki Programming in Lua na lua.org ). Czy to eliminuje potrzebę uniknięcia przeciążenia __eq dla zera?
SansWit
-1

spróbuj węża, pracuj dla mnie

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
źródło
-2

Co powiesz na to ?

if endmyTable[1] == nil then
  -- myTable is empty
end
Venkat Reddy
źródło
1
To nie zadziała na stole, który ma ciągi znaków jak indeks
SamHoque
-3

Wiem, że to jest stare i mogę cię jakoś źle zrozumieć, ale jeśli chcesz, aby stół był pusty, to znaczy, chyba że po prostu sprawdzasz, czy tak jest i nie chcesz lub nie potrzebujesz, aby był pusty, możesz go wyczyścić, po prostu odtwarzając go, chyba że się mylę. można to zrobić za pomocą poniższej składni.

yourtablename = {} -- this seems to work for me when I need to clear a table.
Michael reece
źródło
4
To nie jest pytanie.
Yu Hao
-6

Spróbuj użyć #. Zwraca wszystkie instancje, które znajdują się w tabeli. Jeśli w tabeli nie ma instancji, zwraca0

if #myTable==0 then
print('There is no instance in this table')
end
arturgps2
źródło
1
Pytający mówi, że #to nie wystarczy i podaje powody; czy możesz wyjaśnić, dlaczego to omija te powody?
ameed
cóż ... nie wiem Jestem w tym nowy, więc jedyny sposób, w jaki wiem, to #
arthurgps2