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 {}.).

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

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.


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

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

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

-2

Co powiesz na to ?

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

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.

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

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
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.