Domanda

Recentemente ho scritto un po 'di codice Lua qualcosa del tipo:

local a = {}
for i = 1, n do
   local copy = a
   -- alter the values in the copy
end

Ovviamente, non era quello che volevo fare poiché le variabili contengono riferimenti a una tabella anonima e non i valori della tabella stessa in Lua. Questo è chiaramente definito in Programmazione in Lua , ma me ne ero dimenticato.

Quindi la domanda è cosa devo scrivere invece di copy = a per ottenere una copia dei valori in a ?

È stato utile?

Soluzione

Per giocare un po 'di golf leggibile-codice, ecco una versione breve che gestisce i casi complicati standard:

  • tabelle come chiavi,
  • conservazione dei metatables e
  • tabelle ricorsive.

Possiamo farlo in 7 righe:

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

Esiste una breve descrizione delle operazioni di copia profonda di Lua in questa sintesi .

Un altro riferimento utile è questa pagina wiki degli utenti Lua , che include un esempio su come evitare il __pairs metamethod.

Altri suggerimenti

La copia della tabella ha molte potenziali definizioni. Dipende se desideri una copia semplice o approfondita, se vuoi copiare, condividere o ignorare metatables, ecc. Non esiste un'unica implementazione in grado di soddisfare tutti.

Un approccio è semplicemente quello di creare una nuova tabella e duplicare tutte le coppie chiave / valore:

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)

Nota che dovresti usare coppie invece di ipairs , dal momento che ipairs scorre solo su un sottoinsieme delle chiavi della tabella (es. positivo consecutivo chiavi intere che iniziano da una in ordine crescente).

Solo per illustrare il punto, anche il mio table.copy personale presta attenzione ai metatables:

function table.copy(t)
  local u = { }
  for k, v in pairs(t) do u[k] = v end
  return setmetatable(u, getmetatable(t))
end

Non esiste una funzione di copia sufficientemente condivisa per essere chiamata "standard".

La versione completa di Deep Copy, gestendo tutte e 3 le situazioni:

  1. Riferimento circolare tabella
  2. Tasti che sono anche tabelle
  3. MetaTable

La versione generale:

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

O la versione della tabella:

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

Basato sulle lua-users.org/wiki/CopyTable e Alan Yates 'funzioni.

Una versione facoltativa, graficamente generale, ricorsiva:

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

Forse anche la copia metabile dovrebbe essere facoltativa?

Ecco cosa ho effettivamente fatto:

for j,x in ipairs(a) do copy[j] = x end

Dato che menzioni Doub , se le chiavi della tabella non aumentano rigorosamente monotonicamente, dovrebbero essere coppie non ipairs .

Ho anche trovato una funzione deepcopy più robusta :

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

Gestisce tabelle e metatable chiamando se stesso in modo ricorsivo ( che è la sua ricompensa ). Uno dei bit intelligenti è che puoi passargli qualsiasi valore (che sia una tabella o meno) e verrà copiato correttamente. Tuttavia, il costo è che potrebbe potenzialmente traboccare lo stack. Pertanto, potrebbe essere necessaria una funzione ancora più solida (non ricorsiva).

Ma questo è eccessivo per il semplicissimo caso di voler copiare un array in un'altra variabile.

Il progetto (purtroppo leggermente documentato) stdlib ha una serie di preziose estensioni per diverse librerie spedito con la distribuzione Lua standard. Tra questi ci sono diverse varianti sul tema della copia e fusione delle tabelle.

Questa libreria è inclusa anche nella distribuzione Lua per Windows e dovrebbe probabilmente far parte di qualsiasi cassetta degli attrezzi dell'utente serio di Lua.

Una cosa da accertare quando si implementano cose come questa a mano è la corretta gestione dei metatable. Per semplici applicazioni da tavolo come struttura probabilmente non hai metatable e un semplice ciclo che utilizza coppie () è una risposta accettabile. Ma se la tabella viene utilizzata come albero o contiene riferimenti circolari o ha metatable, le cose diventano più complesse.

Non dimenticare che anche le funzioni sono riferimenti, quindi se volessi "copiare" completamente tutti i valori avresti bisogno di ottenere anche funzioni separate; tuttavia, l'unico modo che conosco per copiare una funzione è usare loadstring (string.dump (func)) , che secondo il manuale di riferimento Lua, non funziona per le funzioni con valori upval.

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

Attenzione: la soluzione contrassegnata è INCORRETTO !

Quando la tabella contiene tabelle, verranno comunque utilizzati i riferimenti a tali tabelle. Ho cercato per due ore un errore che stavo commettendo, mentre era a causa dell'utilizzo del codice sopra.

Quindi è necessario verificare se il valore è una tabella o meno. Se lo è, dovresti chiamare table.copy in modo ricorsivo!

Questa è la funzione table.copy corretta:

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

Nota: questo potrebbe anche essere incompleto quando la tabella contiene funzioni o altri tipi speciali, ma è possibile che qualcosa di noi non sia necessario. Il codice sopra è facilmente adattabile per chi ne ha bisogno.

È buono come otterrai per le tabelle di base. Usa qualcosa come deepcopy se devi copiare tabelle con metatables.

Penso che il motivo per cui Lua non abbia 'table.copy ()' nelle sue librerie standard è perché il compito non è preciso da definire. Come mostrato già qui, si può fare una copia "a un livello di profondità" (che hai fatto), una deepcopy con o senza cura di possibili riferimenti duplicati. E poi ci sono metatables.

Personalmente, vorrei comunque che offrissero una funzione integrata. Solo se le persone non fossero soddisfatte della sua semantica, avrebbero bisogno di farlo da sole. Non molto spesso, tuttavia, si ha effettivamente la necessità di una copia per valore.

Nella maggior parte dei casi, quando avevo bisogno di copiare una tabella, volevo avere una copia che non condividesse nulla con l'originale, in modo tale che qualsiasi modifica della tabella originale non abbia alcun impatto sulla copia (e viceversa ).

Tutti gli snippet mostrati finora non riescono a creare una copia per una tabella che può avere chiavi condivise o chiavi con tabelle poiché rimarranno puntate verso la tabella originale. È facile vedere se si tenta di copiare una tabella creata come: a = {}; a [a] = a . La deepcopy a cui fa riferimento Jon si occupa di questo, quindi se è necessario creare una copia reale / completa , deepcopy dovrebbe essere usato.

Utilizza la libreria penlight qui: https://stevedonovan.github.io/Penlight/api/libraries /pl.tablex.html#deepcopy

local pl = require 'pl.import_into'()
local newTable = pl.tablex.deepcopy(oldTable)

Questo potrebbe essere il metodo più semplice:

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"

Nella mia situazione, quando le informazioni nella tabella sono solo dati e altre tabelle (escluse le funzioni, ...), è la seguente riga di codice la soluzione vincente:

local copyOfTable = json.decode( json.encode( sourceTable ) )

Sto scrivendo il codice Lua per un po 'di domotica su un Fibaro Home Center 2. L'implementazione di Lua è molto limitata senza una libreria centrale di funzioni a cui puoi fare riferimento. Ogni funzione deve essere dichiarata nel codice in modo da mantenere il codice utile, quindi le soluzioni a una linea come questa sono favorevoli.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top