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?

War es hilfreich?

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:

  1. Tischkreis Referenz
  2. Tasten, die auch Tabellen sind
  3. 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.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top