Wie überprüfe ich, ob eine Tabelle ein Element in Lua enthält?

97

Gibt es eine Methode, um zu überprüfen, ob eine Tabelle einen Wert enthält? Ich habe meine eigene (naive) Funktion, aber ich habe mich gefragt, ob es dafür etwas "Offizielles" gibt. Oder etwas effizienteres ...

function table.contains(table, element)
  for _, value in pairs(table) do
    if value == element then
      return true
    end
  end
  return false
end

Der Hauptgrund, warum ich diese Funktionen verwende, ist übrigens, Tabellen als Mengen zu verwenden, dh ohne doppelte Elemente. Gibt es noch etwas, das ich gebrauchen könnte?

Wookai
quelle
3
Was bedeutet die Notation _?
Martin
24
Es ist einfach eine "Müll" -Variable mit dem Namen _. pairs()gibt zurück key, value, aber in diesem Beispiel brauche ich nur den Wert. Es ist eine Art Konvention (übernommen im Buch "Programming in Lua" lua.org/pil/index.html ), diese _Variable zum Speichern von Dingen zu verwenden, die Sie nicht benötigen.
Wookai
Ich habe die Konvention gesehen, "Garbage" -Variablen zu benennen, _die auch in Python und JavaScript verwendet werden.
Iono

Antworten:

116

Sie können die Werte als Schlüssel für die Tabelle festlegen. Beispielsweise:

function addToSet(set, key)
    set[key] = true
end

function removeFromSet(set, key)
    set[key] = nil
end

function setContains(set, key)
    return set[key] ~= nil
end

Es gibt ein voll funktionsfähiges Beispiel hier .

Interjay
quelle
13
Ein anonymer Benutzer hat die folgende Korrektur für Ihren Code vorgeschlagen: Wenn der Wert in der Menge mit dem angegebenen Schlüssel FALSE ist, gibt die Funktion setContains () ein false zurück, obwohl die Tabelle ein Element mit dem angegebenen Schlüssel enthält. Die Zeile "return set [key] ~ = nil" behebt diesen Fehler.
Oers
Vielleicht auchfunction keysOfSet(set) local ret={} for k,_ in pairs(set) do ret[#ret+1]=k end return ret end
Jesse Chisholm
24

In Anbetracht Ihrer Darstellung ist Ihre Funktion so effizient wie möglich. Wie von anderen angemerkt (und in Sprachen, die älter als Lua sind), besteht die Lösung für Ihr eigentliches Problem natürlich darin, die Darstellung zu ändern. Wenn Sie Tabellen haben und Mengen möchten, wandeln Sie Tabellen in Mengen um, indem Sie das Mengenelement als Schlüssel und trueals Wert verwenden. +1 zu interjay.

Norman Ramsey
quelle
2

Ich kann mir keine andere Möglichkeit vorstellen, Werte zu vergleichen, aber wenn Sie das Element der Menge als Schlüssel verwenden, können Sie den Wert auf etwas anderes als Null setzen. Dann erhalten Sie schnelle Suchvorgänge, ohne die gesamte Tabelle durchsuchen zu müssen.

Joel
quelle
2

Ich weiß, dass dies ein alter Beitrag ist, aber ich wollte etwas für die Nachwelt hinzufügen. Die einfache Möglichkeit, das Problem zu lösen, besteht darin, eine weitere Tabelle mit einem Wert für den Schlüssel zu erstellen.

dh. Sie haben 2 Tabellen mit demselben Wert, von denen eine in eine Richtung und eine in die andere Richtung zeigt.

function addValue(key, value)
    if (value == nil) then
        removeKey(key)
        return
    end
    _primaryTable[key] = value
    _secodaryTable[value] = key
end

function removeKey(key)
    local value = _primaryTable[key]
    if (value == nil) then
        return
    end
    _primaryTable[key] = nil
    _secondaryTable[value] = nil
end

function getValue(key)
    return _primaryTable[key]
end

function containsValue(value)
    return _secondaryTable[value] ~= nil
end

Anschließend können Sie die neue Tabelle abfragen, um festzustellen, ob sie den Schlüssel 'element' enthält. Dies verhindert, dass jeder Wert der anderen Tabelle durchlaufen werden muss.

Wenn sich herausstellt, dass Sie das 'Element' nicht als Schlüssel verwenden können, weil es beispielsweise keine Zeichenfolge ist, fügen tostringSie beispielsweise eine Prüfsumme hinzu oder verwenden Sie diese als Schlüssel.

Warum willst du das tun? Wenn Ihre Tabellen sehr groß sind, ist die Zeit zum Durchlaufen aller Elemente erheblich, sodass Sie dies nicht sehr oft tun können. Der zusätzliche Speicheraufwand ist relativ gering, da 2 Zeiger auf dasselbe Objekt und nicht 2 Kopien desselben Objekts gespeichert werden. Wenn Ihre Tabellen sehr klein sind, ist dies viel weniger wichtig. Tatsächlich kann die Iteration sogar schneller sein als die Suche nach einer anderen Karte.

Der Wortlaut der Frage deutet jedoch stark darauf hin, dass Sie eine große Anzahl von Punkten zu behandeln haben.

James
quelle
Eine gute Erklärung, fügt aber der Diskussion nichts hinzu. Es wäre wahrscheinlich eine bessere Idee gewesen, die Antwort von interjay zu bearbeiten.
BCDAN
1
Außerdem sollte '.key' überall in diesem Code durch '[key]' ersetzt werden (dasselbe gilt für 'value')
Njol