Wie erhalte ich die Anzahl der Einträge in einer Lua-Tabelle?

131

Klingt nach einer Frage "Lass es mich für dich googeln", aber irgendwie kann ich keine Antwort finden. Der Lua- #Operator zählt nur Einträge mit Ganzzahlschlüsseln, und zwar auch 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"

Wie erhalte ich die Anzahl aller Einträge, ohne sie zu zählen?

Roman Starkov
quelle
3
@lhf: Ich habe einen Serializer geschrieben, der sich an jedes Objekt erinnert, das er gesehen hat, und beim nächsten Mal eine ganzzahlige Referenz anstelle des Objekts ausgibt. Der natürliche Weg , dies zu schreiben , ist so etwas wie dictionary[value] = #dictionary + 1, wo #die Anzahl der repräsentiert alle Objekte. Was ich frage mich, warum Sie nicht diese wollen: in allen vernünftigen Anwendungsfälle für # (siehe Antwort von kaizer.se), die Anzahl aller Objekte genau gleich was # bereits zurückkehrt; Es scheint, als wäre es eine Verbesserung, # alles zählen zu lassen. Natürlich bin ich ein Lua-Neuling und könnte den Punkt verfehlen.
Roman Starkov
32
@lhf: Es ist nicht nett von Ihnen, die Kompetenz des Programmierers in Frage zu stellen, indem Sie fragen, warum er etwas tun muss, für das alle vernünftigen Programmiersprachen eine einfache Funktion haben.
Timwi
5
@ Timwi: Es ist nicht nett von Ihnen, einem der Lua-Sprachautoren zu sagen, dass Lua nicht zu den "vernünftigen" Programmiersprachen gehört. ;-) Übrigens habe ich diese Informationen auch nie gebraucht.
Alexander Gladysh
5
Ich glaube nicht , dass ich jemals verwendet jedes Merkmal einer einzigen Sprache. Das bedeutet nicht, dass sie für andere nicht nützlich sind :)
Roman Starkov
7
@ Sylvanaar Meiner Meinung nach ist der #Operator nur schlecht definiert. Dies ist so einfach zu beheben: Erstens #deterministisch machen und zweitens einen neuen Operator oder eine neue Funktion einführen, um die verdammte Anzahl zu erhalten. Ende der Geschichte ... Warum müssen sie so stur sein? :)
Roman Starkov

Antworten:

129

Sie haben bereits die Lösung in der Frage - die einzige Möglichkeit besteht darin, die gesamte Tabelle mit zu iterieren pairs(..).

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

Beachten Sie auch, dass die Definition des Operators "#" etwas komplizierter ist. Lassen Sie mich das anhand dieser Tabelle veranschaulichen:

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

Gemäß dem Handbuch, jeder von 3, 5 und 9 sind gültige Ergebnisse für #t. Die einzig vernünftige Möglichkeit, es zu verwenden, sind Arrays eines zusammenhängenden Teils ohne Nullwerte.

u0b34a0f6ae
quelle
42
Ich schaudere immer noch bei der Erinnerung an meine Erfahrung mit Lua, als ich zum ersten Mal feststellte, dass der Rückgabewert eines Basisoperators wie #nicht deterministisch ist.
Roman Starkov
5
Oh, es ist wahrscheinlich deterministisch. Es ist genau das Gleiche wie wenn der C-Standard etwas für die Implementierung definiertes Verhalten hinterlässt. Der Grund dafür ist, dass verschiedene Implementierer unterschiedliche Implementierungsoptionen auswählen können.
Nakedible
19
According to the manual, any of 3, 5 and 9 are valid results for #t. Laut Handbuch ist das Aufrufen von # für Nichtsequenzen undefiniert . Das bedeutet, dass jedes Ergebnis (-1, 3, 3.14, 5, 9) gültig ist.
cubuspl42
6
In Bezug auf gültige Ergebnisse: u0b34a0f6ae ist für Lua 5.1 korrekt, während cubuspl42 für Lua 5.2 korrekt ist. In jedem Fall ist das Ganze völlig verrückt.
Jeremy
9
Die Tatsache, dass # in einer Nicht-Sequenz keine Ausnahme generiert, ist nur eines der Dinge, die die Verwendung von lua ein bisschen so machen, als würde man sich schneiden, um sich besser zu fühlen.
Bootscodierer
20

Sie können eine Metatabelle einrichten, um die Anzahl der Einträge zu verfolgen. Dies ist möglicherweise schneller als die Iteration, wenn diese Informationen häufig benötigt werden.

Ergosys
quelle
Gibt es eine bequeme Möglichkeit, Einträge mit dieser Methode zu löschen?
u0b34a0f6ae
Leider scheint die Funktion __newindex bei Nullzuweisungen nur dann ausgelöst zu werden, wenn der Index nicht vorhanden ist. Sie müssen also die Entfernung von Einträgen über eine spezielle Funktion durchführen.
Ergosys
1
Sie sollten Daten in einer separaten Tabelle speichern (z. B. als Upvalue für __index und __newindex verfügbar). Dann würden sowohl __index als auch __newindex für jeden Tabellenzugriff ausgelöst. Sie sollten jedoch überprüfen, ob die Leistung akzeptabel ist.
Alexander Gladysh
@Alexander: Ah ja, und dann der nächste Stolperpunkt: Wenn Sie die Tabelle vertreten, funktioniert die normale Iteration durch Paare nicht. Dies wird in Lua 5.2 möglich sein, hörte ich.
u0b34a0f6ae
In 5.2 gibt es __pairs- und __ipairs-Metamethoden ... Wenn Sie dies in 5.1 tun möchten, müssen Sie die Funktion pair () durch Ihre eigene ersetzen. Aber das ist wahrscheinlich zu viel. :-)
Alexander Gladysh
3

Es gibt einen Weg, der jedoch enttäuschend sein kann: Verwenden Sie eine zusätzliche Variable (oder eines der Tabellenfelder) zum Speichern der Anzahl und erhöhen Sie sie jedes Mal, wenn Sie eine Einfügung vornehmen.

count = 0
tbl = {}

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

tbl[1] = 48
count = count + 1

print(count)   -- prints "2"

Es gibt keine andere Möglichkeit, der Operator # funktioniert nur bei Array-ähnlichen Tabellen mit aufeinanderfolgenden Schlüsseln.

Kikito
quelle
3
Dies kann mit einer Proxy-Tabelle und Metamethoden automatisiert werden, wie in der Antwort
RBerteig
Ich habe den Eindruck von den Kommentaren bekommen, dass die Sache mit den Proxytables / Metamethoden dieses Szenario noch nicht vollständig unterstützt, daher akzeptiere ich dies als den derzeit besten verfügbaren Weg.
Roman Starkov
Das Zählen ist die einzige Möglichkeit für Tabellen, und das Hinzufügen von Zeilen beim Erstellen der Tabellen ist besser als eine Funktion, um sie jedes Mal zu zählen, wenn Sie die Zählung benötigen. Sie können am Ende einen Schlüssel hinzufügen, dessen Wert auf die Anzahl festgelegt ist.
Henrik Erlandsson
2

Sie könnten die Taschenlampenbibliothek verwenden . Dies hat eine Funktion, sizedie die tatsächliche Größe der Tabelle angibt.

Es hat viele der Funktionen implementiert, die wir möglicherweise beim Programmieren benötigen und die in Lua fehlen.

Hier ist das Beispiel für die Verwendung.

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

> #a
0

> tablex.size(a)
3

quelle
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)
Schlaffrau
quelle
1
Wenn Sie eine Antwort veröffentlichen, wird empfohlen, die minimale Menge an Code zu veröffentlichen, die eine Frage direkt beantwortet, und zu erläutern, wie der Code die Frage beantwortet. Siehe hier .
cst1992
__newindexRufen Sie nur auf, wenn ein neuer Schlüssel definiert ist. Daher besteht keine Möglichkeit zum Aufrufen, __newindexwenn nilein vorhandener Schlüssel festgelegt ist.
Frank AK
1

Der einfachste Weg, die Anzahl der Einträge in einer Tabelle zu ermitteln, ist mit '#'. #tableName erhält die Anzahl der Einträge, solange sie nummeriert sind:

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

Leider funktioniert es nicht, wenn sie nicht nummeriert sind.

Surge12
quelle
-1

Wenn die Elemente der Tabelle mit der Einfügemethode hinzugefügt werden, wird getn anscheinend korrekt zurückgegeben. Ansonsten müssen wir alle Elemente zählen

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

Es werden 2 korrekt gedruckt

Yongxin Zhang
quelle