Jak uzyskać liczbę wpisów w tabeli Lua?

139

Brzmi jak pytanie „pozwól mi to wygooglować”, ale jakoś nie mogę znaleźć odpowiedzi. #Operator Lua liczy tylko wpisy z kluczami całkowitymi, podobnie jak table.getn:

tbl = {}
tbl["test"] = 47
tbl[1] = 48
print(#tbl, table.getn(tbl))   -- prints "1     1"

count = 0
for _ in pairs(tbl) do count = count + 1 end
print(count)            -- prints "2"

Jak uzyskać liczbę wszystkich zgłoszeń bez ich liczenia?

Roman Starkov
źródło
3
@lhf: Napisałem serializator, który pamięta każdy obiekt, który widział, i gdy następnym razem go zobaczy, zamiast obiektu emituje odwołanie do liczby całkowitej. Naturalnym sposobem zapisania tego jest coś w rodzaju dictionary[value] = #dictionary + 1, gdzie #reprezentuje liczbę wszystkich obiektów. Co ja zastanawiam się, dlaczego ty nie chcesz tego: w każdym sane przypadków użycia dla # (patrz odpowiedź do kaizer.se), liczba wszystkich obiektów jest dokładnie równa co już # zwrotów; wygląda na to, że # policzyć wszystko jest wyłącznie poprawą. Oczywiście jestem nowicjuszem w Lua i mogę nie mieć sensu.
Roman Starkov
32
@lhf: Nie jest miło z twojej strony kwestionować kompetencji programisty, pytając, dlaczego musi on zrobić coś, do czego wszystkie rozsądne języki programowania mają prostą funkcję.
Timwi,
5
@Timwi: To nie jest miłe z twojej strony, mówiąc jednemu z autorów języka Lua, że ​​Lua nie należy do "rozsądnych" języków programowania. ;-) Swoją drogą, ja też nigdy nie potrzebowałem tych informacji.
Alexander Gladysh
5
Nie sądzę, żebym kiedykolwiek używał wszystkich funkcji jednego języka. To nie znaczy, że nie są przydatne dla innych :)
Roman Starkov
7
@sylvanaar Moim zdaniem #operator jest po prostu źle zdefiniowany. Jest to tak łatwe do naprawienia: po pierwsze, stwórz #deterministyczne, a po drugie , wprowadź nowy operator lub funkcję, aby uzyskać liczbę cętek. Koniec historii ... Dlaczego oni muszą być tacy uparci? :)
Roman Starkov

Odpowiedzi:

134

Masz już rozwiązanie w pytaniu - jedynym sposobem jest powtórzenie całej tabeli za pomocą pairs(..).

function tablelength(T)
  local count = 0
  for _ in pairs(T) do count = count + 1 end
  return count
end

Zauważ też, że definicja operatora „#” jest nieco bardziej skomplikowana. Zilustruję to na podstawie poniższej tabeli:

t = {1,2,3}
t[5] = 1
t[9] = 1

Zgodnie z instrukcją, każdy z 3, 5 i 9 to ważne wyniki dla #t. Jedynym rozsądnym sposobem użycia jest tablice jednej ciągłej części bez wartości zerowych.

u0b34a0f6ae
źródło
43
Wciąż wzdrygam się na wspomnienie mojego doświadczenia z Lua, kiedy po raz pierwszy zdałem sobie sprawę, że wartość zwracana przez operatora podstawowego, takiego jak, #nie jest deterministyczna.
Roman Starkov
6
Och, to prawdopodobnie deterministyczne. Jest to dokładnie to samo, co wtedy, gdy standard C pozostawia coś do zdefiniowania w ramach implementacji. Dzieje się tak, ponieważ różni realizatorzy mogą wybierać różne opcje implementacji.
Nakedible
21
According to the manual, any of 3, 5 and 9 are valid results for #t. Zgodnie z instrukcją wywoływanie # na niesekwencjach jest niezdefiniowane . Oznacza to, że każdy wynik (-1, 3, 3,14, 5, 9) jest prawidłowy.
cubuspl42
6
Odnośnie poprawnych wyników: u0b34a0f6ae jest poprawne dla Lua 5.1, a cubuspl42 jest poprawne dla Lua 5.2. W obu przypadkach cała sprawa jest całkowicie szalona.
Jeremy
9
Fakt, że # na nie-sekwencji nie generuje wyjątku, to tylko jedna z rzeczy, która sprawia, że ​​używanie lua jest trochę jak cięcie siebie, aby poczuć się lepiej.
boatcoder
21

Możesz skonfigurować metatabelę, aby śledzić liczbę wpisów, może to być szybsze niż iteracja, jeśli te informacje są często potrzebne.

ergosys
źródło
Czy istnieje wygodny sposób usuwania wpisów za pomocą tej metody?
u0b34a0f6ae
Niestety, wygląda na to, że funkcja __newindex nie jest uruchamiana w przypadku przypisań zerowych, chyba że indeks nie istnieje, więc wydaje się, że musisz usunąć pozycję lejka za pomocą specjalnej funkcji.
ergosys
1
Dane należy przechowywać w osobnej tabeli (na przykład dostępne jako upvalue dla __index i __newindex). Wtedy zarówno __index, jak i __newindex będą uruchamiane przy każdym dostępie do tabeli. Powinieneś jednak sprawdzić, czy wydajność jest akceptowalna.
Alexander Gladysh
@Alexander: Ach tak, a potem kolejny problem: jeśli proxy tabeli, to normalna iteracja parami nie działa. Słyszałem, że będzie to możliwe do rozwiązania w Lua 5.2.
u0b34a0f6ae
Będą metametody __pairs i __ipairs w 5.2 ... Jeśli chcesz to zrobić w 5.1, musisz zamienić funkcję pairs () na własną. Ale to chyba za dużo. :-)
Alexander Gladysh
4

Jest jeden sposób, ale może być rozczarowujący: użyj dodatkowej zmiennej (lub jednego z pól tabeli) do przechowywania liczby i zwiększaj ją za każdym razem, gdy dokonujesz wstawienia.

count = 0
tbl = {}

tbl["test"] = 47
count = count + 1

tbl[1] = 48
count = count + 1

print(count)   -- prints "2"

Nie ma innego sposobu, operator # będzie działał tylko na tablicach przypominających tablice z kolejnymi kluczami.

kikito
źródło
3
Można to zautomatyzować za pomocą tabeli proxy i metametod, jak wspomniano w odpowiedzi
ergosys
Odniosłem wrażenie na podstawie komentarzy, że funkcja proxytable / metamethods nie obsługuje jeszcze w pełni tego scenariusza, więc zaakceptuję to jako najlepszy obecnie dostępny sposób.
Roman Starkov
Liczenie jest jedynym sposobem w przypadku tabel, a dodawanie wierszy podczas tworzenia tabel jest lepsze niż funkcja liczenia ich za każdym razem, gdy jest to potrzebne. Możesz dodać klucz na końcu z wartością ustawioną na licznik.
Henrik Erlandsson
2

Najprostszym sposobem, jaki znam, aby uzyskać liczbę wpisów w tabeli, jest użycie znaku „#”. #tableName pobiera liczbę wpisów, o ile są one ponumerowane:

tbl={
    [1]
    [2]
    [3]
    [4]
    [5]
}
print(#tbl)--prints the highest number in the table: 5

Niestety, jeśli nie są ponumerowane, to nie zadziała.

Surge12
źródło
2

Możesz użyć biblioteki Penlight . Ma funkcję, sizektóra podaje rzeczywisty rozmiar stołu.

Zaimplementował wiele funkcji, których możemy potrzebować podczas programowania i których brakuje w Lua.

Oto przykład użycia go.

> tablex = require "pl.tablex"
> a = {}
> a[2] = 2
> a[3] = 3 
> a['blah'] = 24

> #a
0

> tablex.size(a)
3

źródło
1
local function CountedTable(x)
    assert(type(x) == 'table', 'bad parameter #1: must be table')

    local new_t = {}
    local mt = {}

    -- `all` will represent the number of both
    local all = 0
    for k, v in pairs(x) do
        all = all + 1
    end

    mt.__newindex = function(t, k, v)
        if v == nil then
            if rawget(x, k) ~= nil then
                all = all - 1
            end
        else
            if rawget(x, k) == nil then
                all = all + 1
            end
        end

        rawset(x, k, v)
    end

    mt.__index = function(t, k)
        if k == 'totalCount' then return all
        else return rawget(x, k) end
    end

    return setmetatable(new_t, mt)
end

local bar = CountedTable { x = 23, y = 43, z = 334, [true] = true }

assert(bar.totalCount == 4)
assert(bar.x == 23)
bar.x = nil
assert(bar.totalCount == 3)
bar.x = nil
assert(bar.totalCount == 3)
bar.x = 24
bar.x = 25
assert(bar.x == 25)
assert(bar.totalCount == 4)
Sleepwom
źródło
1
Podczas publikowania odpowiedzi zaleca się opublikowanie minimalnej ilości kodu, który bezpośrednio odpowiada na pytanie, i wyjaśnienie, w jaki sposób kod odpowiada na pytanie. Zobacz tutaj .
cst1992
__newindexwywołanie tylko wtedy, gdy zdefiniowany jest nowy klucz, więc nie ma szans na wywołanie, __newindexgdy ustawimy nilistniejący klucz.
Frank AK
0

wydaje się, że kiedy elementy tabeli zostaną dodane metodą insert, to getn zwróci poprawnie. W przeciwnym razie musimy policzyć wszystkie elementy

mytable = {}
element1 = {version = 1.1}
element2 = {version = 1.2}
table.insert(mytable, element1)
table.insert(mytable, element2)
print(table.getn(mytable))

Wydrukuje 2 poprawnie

Yongxin Zhang
źródło
0

Natknąłem się na ten wątek i chcę opublikować inną opcję. Używam Luada wygenerowanego z kontrolera bloków, ale zasadniczo działa poprzez sprawdzanie wartości w tabeli, a następnie zwiększanie wartości sprawdzanej o 1. W końcu tabela się wyczerpie, a wartość w tym indeksie wyniesie zero.

Więc odejmij 1 od indeksu, który zwrócił zero, i to jest rozmiar tabeli.

Mam zmienną globalną dla TableSize, która jest ustawiona na wynik tej liczby.

function Check_Table_Size()
  local Count = 1
  local CurrentVal = (CueNames[tonumber(Count)])
  local repeating = true
  print(Count)
  while repeating == true do
    if CurrentVal ~= nil then
      Count = Count + 1
      CurrentVal = CueNames[tonumber(Count)]
     else
      repeating = false
      TableSize = Count - 1
    end
  end
  print(TableSize)
end
Vincent
źródło