Wie kopiert man eine Lua-Tabelle nach Wert?
Frage
Kürzlich habe ich etwas Lua-Code geschrieben, etwa:
local a = {}
for i = 1, n do
local copy = a
-- alter the values in the copy
end
Offensichtlich war das nicht das, was ich tun wollte, da Variablen Verweise auf eine anonyme Tabelle enthalten und nicht die Werte der Tabelle selbst in Lua.Dies ist in klar dargelegt Programmieren in Lua, aber ich hatte es vergessen.
Die Frage ist also, was soll ich stattdessen schreiben copy = a
um eine Kopie der Werte zu erhalten a
?
Lösung
Um einen wenig lesbarer-Code-Golf zu spielen, hier ist eine kurze Version, die den Standard kniffligen Fälle behandelt:
- Tabellen als Schlüssel,
- Erhaltung Metatables und
- rekursive Tabellen.
Wir können dies in 7 Zeilen:
function copy(obj, seen)
if type(obj) ~= 'table' then return obj end
if seen and seen[obj] then return seen[obj] end
local s = seen or {}
local res = setmetatable({}, getmetatable(obj))
s[obj] = res
for k, v in pairs(obj) do res[copy(k, s)] = copy(v, s) end
return res
end
Es gibt eine kurze Zuschreibung von Lua tiefKopierVorgänge in dieser Kern .
Eine weitere nützliche Referenz diese Lua-Benutzer Wiki-Seite , die sich auf ein Beispiel enthält, wie die __pairs
metamethod zu vermeiden.
Andere Tipps
Tabelle Kopie hat viele mögliche Definitionen. Es hängt davon ab, ob Sie einfache oder tiefe Kopie, ob Sie kopieren möchten, zu teilen oder Metatables ignorieren usw. Es gibt keine einzige Implementierung, die alle zufrieden stellen können.
Ein Ansatz ist es, einfach eine neue Tabelle zu erstellen und kopieren Sie alle Schlüssel / Wert-Paare:
function table.shallow_copy(t)
local t2 = {}
for k,v in pairs(t) do
t2[k] = v
end
return t2
end
copy = table.shallow_copy(a)
Beachten Sie, dass pairs
statt ipairs
, da ipairs
verwenden sollten nur eine Teilmenge des Tabellenschlüssels durchlaufen (dh. In Folge positive ganz Zahl Tasten an einem Start in aufsteigender Reihenfolge).
Sie einfach den Punkt zu illustrieren, mein persönlicher table.copy
achtet auch auf Metatables:
function table.copy(t)
local u = { }
for k, v in pairs(t) do u[k] = v end
return setmetatable(u, getmetatable(t))
end
Es ist ausreichend keine Kopierfunktion weit vereinbart genannt „Standard“ werden.
Die Vollversion von tiefer Kopie, all drei Situationen Handhabung:
- Tischkreis Referenz
- Tasten, die auch Tabellen sind
- MetaTable
Die allgemeine Version:
local function deepcopy(o, seen)
seen = seen or {}
if o == nil then return nil end
if seen[o] then return seen[o] end
local no
if type(o) == 'table' then
no = {}
seen[o] = no
for k, v in next, o, nil do
no[deepcopy(k, seen)] = deepcopy(v, seen)
end
setmetatable(no, deepcopy(getmetatable(o), seen))
else -- number, string, boolean, etc
no = o
end
return no
end
oder die Tabelle Version:
function table.deepcopy(o, seen)
seen = seen or {}
if o == nil then return nil end
if seen[o] then return seen[o] end
local no = {}
seen[o] = no
setmetatable(no, deepcopy(getmetatable(o), seen))
for k, v in next, o, nil do
k = (type(k) == 'table') and k:deepcopy(seen) or k
v = (type(v) == 'table') and v:deepcopy(seen) or v
no[k] = v
end
return no
end
Basierend auf der lua-users.org/wiki/CopyTable 's und Alan Yates ‘Funktionen.
Eine optional tief, graphen allgemeine rekursiven Version:
function table.copy(t, deep, seen)
seen = seen or {}
if t == nil then return nil end
if seen[t] then return seen[t] end
local nt = {}
for k, v in pairs(t) do
if deep and type(v) == 'table' then
nt[k] = table.copy(v, deep, seen)
else
nt[k] = v
end
end
setmetatable(nt, table.copy(getmetatable(t), deep, seen))
seen[t] = nt
return nt
end
Vielleicht Metatabelle Kopie sollte auch optional sein?
Hier ist, was ich eigentlich tat:
for j,x in ipairs(a) do copy[j] = x end
Wie Doub erwähnt , wenn Ihre Tabellenschlüssel sind nicht streng monoton steigend, sollte es nicht pairs
werden ipairs
.
Ich fand auch eine deepcopy
Funktion, die robuster ist:
function deepcopy(orig)
local orig_type = type(orig)
local copy
if orig_type == 'table' then
copy = {}
for orig_key, orig_value in next, orig, nil do
copy[deepcopy(orig_key)] = deepcopy(orig_value)
end
setmetatable(copy, deepcopy(getmetatable(orig)))
else -- number, string, boolean, etc
copy = orig
end
return copy
end
Es behandelt Tabellen und Metatables von sich selbst rekursiv Aufruf (, die seine eigene Belohnung ist). Einer der cleveren Bits ist, dass Sie es jeder beliebige Wert (ob eine Tabelle oder nicht) passieren kann, und es wird korrekt kopiert werden. Allerdings sind die Kosten, dass sie möglicherweise den Stack überlaufen könnte. So und noch robuster (nicht-rekursive) funktionieren erforderlich sein könnten.
Aber das ist übertrieben für den sehr einfachen Fall will ein Array in einer anderen Variable kopieren.
Das (leider leicht dokumentiert) stdlib Projekt hat eine Reihe von wertvollen Erweiterungen mehrere der Bibliotheken mit der Standard-Lua-Distribution ausgeliefert. Unter ihnen sind mehrere Variationen über das Thema der Tabelle kopieren und Verschmelzung.
Diese Bibliothek auch im Lua für Windows Verteilung und sollte wahrscheinlich ein Teil jeder sein ernsthafte Toolbox Lua Benutzer.
Eine Sache, um sicherzustellen, dass der, wenn Dinge wie diese von Hand Umsetzung ist die richtige Handhabung der Metatables. Für eine einfache Tabelle-as-Struktur Anwendungen, die Sie wahrscheinlich haben noch keine Metatables, und eine einfache Schleife pairs()
verwendet, ist eine akzeptable Antwort. Aber wenn die Tabelle als ein Baum verwendet wird, oder enthält zirkuläre Referenzen oder Metatables hat, dann werden die Dinge komplizierter.
Vergessen Sie nicht, dass Funktionen auch Referenzen sind. Wenn Sie also alle Werte vollständig „kopieren“ wollten, müssten Sie auch separate Funktionen erhalten;Die einzige mir bekannte Möglichkeit, eine Funktion zu kopieren, ist jedoch die Verwendung loadstring(string.dump(func))
, was laut Lua-Referenzhandbuch nicht für Funktionen mit Aufwärtswerten funktioniert.
do
local function table_copy (tbl)
local new_tbl = {}
for key,value in pairs(tbl) do
local value_type = type(value)
local new_value
if value_type == "function" then
new_value = loadstring(string.dump(value))
-- Problems may occur if the function has upvalues.
elseif value_type == "table" then
new_value = table_copy(value)
else
new_value = value
end
new_tbl[key] = new_value
end
return new_tbl
end
table.copy = table_copy
end
Achtung: die markierte Lösung FALSCH
Wenn die Tabelle Tabellen enthält, Verweise auf diese Tabellen noch stattdessen verwendet werden. Ich habe einen Fehler 2 Stunden gesucht, die ich machte, während es wegen der Verwendung des obigen Code war.
So können Sie überprüfen müssen, wenn der Wert eine Tabelle ist oder nicht. Wenn ja, sollten Sie table.copy rekursiv rufen!
Dies ist die richtige table.copy Funktion:
function table.copy(t)
local t2 = {};
for k,v in pairs(t) do
if type(v) == "table" then
t2[k] = table.copy(v);
else
t2[k] = v;
end
end
return t2;
end
Hinweis: Dies könnte auch unvollständig sein, wenn die Tabellenfunktionen oder andere spezielle Arten enthalten, aber das ist möglich, etwas, die meisten von uns nicht brauchen. Der obige Code ist leicht anpassbar für diejenigen, die es brauchen.
Das ist so gut, wie Sie für Basistabellen erhalten werden. Verwenden Sie so etwas wie deep wenn Sie Tabellen mit Metatables kopieren müssen.
Ich denke, der Grund, warum Lua nicht ‚table.copy ()‘ in seinen Standardbibliotheken ist, weil die Aufgabe nicht genau zu definieren ist. Wie bereits hier gezeigt wird, kann entweder eine Kopie „eine Ebene tiefer“ machen (was Sie getan haben), ein deep mit oder ohne Fürsorge möglicher doppelter Referenzen. Und dann gibt es Metatables.
Persönlich würde Ich mag sie nach wie vor eine integrierte Funktion bieten. Nur wenn die Menschen mit ihrer Semantik nicht zufrieden sein würden, müßten sie es selbst gehen zu tun. Nicht sehr oft, aber man hat eigentlich die Kopie-by-Wert benötigen.
In den meisten Fällen, wenn ich eine Tabelle zu kopieren brauchte, wollte ich eine Kopie haben, die mit dem Original nichts teilen, so dass jede Änderung der ursprünglichen Tabelle hat keine Auswirkungen auf der Kopie (und umgekehrt ).
Alle Schnipsel, die eine Kopie für einen Tisch zu schaffen, die Schlüssel oder Schlüssel mit Tabellen geteilt haben, können als diejenigen gehen gelassen werden Hinweis auf die ursprüngliche Tabelle nicht so weit gezeigt wurden. Es ist leicht zu sehen, wenn Sie versuchen, eine Tabelle wie geschaffen zu kopieren: a = {}; a[a] = a
. deep Funktion von Jon verwiesen kümmert sich darum, wenn Sie also eine echte / vollständige Kopie erstellen müssen , deepcopy
verwendet werden soll.
Verwenden penlight Bibliothek hier: https://stevedonovan.github.io/Penlight/api/libraries /pl.tablex.html#deepcopy
local pl = require 'pl.import_into'()
local newTable = pl.tablex.deepcopy(oldTable)
Dies könnte die einfachste Methode sein:
local data = {DIN1 = "Input(z)", DIN2 = "Input(y)", AINA1 = "Input(x)"}
function table.copy(mytable) --mytable = the table you need to copy
newtable = {}
for k,v in pairs(mytable) do
newtable[k] = v
end
return newtable
end
new_table = table.copy(data) --copys the table "data"
In meiner Situation, wenn die Information in der Tabelle nur Daten und andere Tabellen ist (mit Ausnahme von Funktionen, ...), sind die folgende Codezeile des Gewinn Lösung:
local copyOfTable = json.decode( json.encode( sourceTable ) )
Ich schreibe Lua-Code für einige Hausautomation auf einem Fibaro Home Center 2. Die Umsetzung der Lua sehr ist ohne zentrale Bibliothek von Funktionen beschränken Sie sich beziehen können. Jede Funktion muss so im Code deklariert werden, um den Code zu wart zu halten, so dass eine Zeile Lösungen wie diese sind günstig.