lua - toda - pegar como tabla anidada
¿Cómo se copia una tabla Lua por valor? (15)
Advertencia: la solución marcada es INCORRECTA !
Cuando la tabla contiene tablas, las referencias a esas tablas se seguirán utilizando en su lugar. He estado buscando durante dos horas un error que estaba cometiendo, mientras era debido al uso del código anterior.
Así que necesitas verificar si el valor es una tabla o no. Si es así, deberías llamar a table.copy recursivamente!
Esta es la función correcta de table.copy:
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: Esto también puede estar incompleto cuando la tabla contiene funciones u otros tipos especiales, pero es posible que la mayoría de nosotros no lo necesitemos. El código anterior es fácilmente adaptable para aquellos que lo necesitan.
Recientemente escribí un poco de código Lua algo así como:
local a = {}
for i = 1, n do
local copy = a
-- alter the values in the copy
end
Obviamente, eso no era lo que quería hacer, ya que las variables contienen referencias a una tabla anónima, no a los valores de la tabla en Lua. Esto está claramente establecido en Programación en Lua , pero lo había olvidado.
Entonces, la pregunta es: ¿qué debo escribir en lugar de copy = a
para obtener una copia de los valores en a
?
Copia de tabla tiene muchas definiciones potenciales. Depende de si desea una copia simple o profunda, si desea copiar, compartir o ignorar los metatables, etc. No existe una implementación única que pueda satisfacer a todos.
Un enfoque es simplemente crear una nueva tabla y duplicar todos los pares clave / valor:
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)
Tenga en cuenta que debe usar pairs
lugar de ipairs
, ya que los ipairs
solo se ipairs
en un subconjunto de las claves de la tabla (es decir, las claves enteras positivas consecutivas que comienzan en una en orden ipairs
).
Creo que la razón por la que Lua no tiene ''table.copy ()'' en sus bibliotecas estándar es porque la tarea no es precisa de definir. Como se muestra aquí, uno puede hacer una copia "a un nivel de profundidad" (lo que usted hizo), una copia profunda con o sin el cuidado de posibles referencias duplicadas. Y luego están los metatables.
Personalmente, me gustaría que ofrecieran una función incorporada. Solo si la gente no estuviera satisfecha con su semántica, tendrían que hacerlo ellos mismos. No muy a menudo, sin embargo, uno realmente tiene la necesidad de copiar por valor.
El proyecto stdlib (lamentablemente ligeramente documentado) tiene varias extensiones valiosas para varias de las bibliotecas incluidas con la distribución estándar de Lua. Entre ellos hay varias variaciones sobre el tema de la copia y fusión de tablas.
Esta biblioteca también se incluye en la distribución de Lua para Windows , y probablemente debería formar parte de la caja de herramientas de cualquier usuario serio de Lua.
Una cosa de la que hay que asegurarse al implementar cosas como esta a mano es el manejo adecuado de los metatables. Para las aplicaciones simples de tabla como estructura, probablemente no tenga metatables, y un simple bucle que usa pairs()
es una respuesta aceptable. Pero si la tabla se utiliza como un árbol, o contiene referencias circulares, o tiene metatables, entonces las cosas se vuelven más complejas.
En la mayoría de los casos en los que necesitaba copiar una tabla, quería tener una copia que no compartiera nada con el original, de modo que cualquier modificación de la tabla original no afecte a la copia (y viceversa).
Todos los fragmentos de código que se han mostrado hasta el momento fallan al crear una copia para una tabla que puede tener claves compartidas o claves con tablas, ya que se dejarán apuntando a la tabla original. Es fácil ver si intenta copiar una tabla creada como: a = {}; a[a] = a
a = {}; a[a] = a
. La función de copia profunda a la que hace referencia Jon se encarga de eso, por lo que si necesita crear una copia real / completa, debe usarse la copia deepcopy
.
En mi situación, cuando la información en la tabla es solo datos y otras tablas (excluyendo funciones, ...), es la siguiente línea de código la solución ganadora:
local copyOfTable = json.decode( json.encode( sourceTable ) )
Estoy escribiendo el código Lua para algunos sistemas de automatización del hogar en Fibaro Home Center 2. La implementación de Lua es muy limitada, ya que no hay una biblioteca central de funciones a las que puedas referirte. Cada función debe ser declarada en el código para mantener el código en servicio, por lo que las soluciones de una línea como esta son favorables.
Eso es tan bueno como para las tablas básicas. Use algo como deepcopy si necesita copiar tablas con metatables.
Este podría ser el método más simple:
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"
Esto es lo que realmente hice:
for j,x in ipairs(a) do copy[j] = x end
Como lo menciona Doub , si las claves de su mesa no son estrictamente monotónicas, deberían ser pairs
no ipairs
.
También encontré una función de deepcopy
que es más 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
Maneja tablas y metatables llamándose a sí mismo de forma recursiva ( que es su propia recompensa ). Uno de los bits inteligentes es que puede pasarle cualquier valor (sea una tabla o no) y se copiará correctamente. Sin embargo, el costo es que potencialmente podría desbordar la pila. Por lo tanto, e incluso una función más robusta (no recursiva) podría ser necesaria.
Pero eso es excesivo para el caso muy simple de querer copiar una matriz en otra variable.
La versión completa de copia profunda, manejando las 3 situaciones:
- Tabla circular de referencia
- Teclas que también son tablas.
- Metatable
La versión general:
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 versión de la mesa:
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
Basado en las funciones de lua-users.org/wiki/CopyTable ''y Alan Yates ''.
No olvide que las funciones también son referencias, por lo que si desea "copiar" por completo todos los valores, también necesitará obtener funciones separadas; sin embargo, la única forma que conozco de copiar una función es usar loadstring(string.dump(func))
, que de acuerdo con el manual de referencia de Lua, no funciona para funciones con valores superiores.
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
Para jugar un poco al golf de código legible, aquí hay una versión corta que maneja los casos difíciles estándar:
- tablas como llaves,
- preservar los metatables, y
- Tablas recursivas.
Podemos hacerlo en 7 líneas:
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
Hay una breve reseña de las operaciones de copia profunda de Lua en esta lista .
Otra referencia útil es esta página wiki de Lua-users , que incluye un ejemplo sobre cómo evitar el __pairs
__pairs.
Solo para ilustrar el punto, mi table.copy
personal.copia también presta atención a los metatables:
function table.copy(t)
local u = { }
for k, v in pairs(t) do u[k] = v end
return setmetatable(u, getmetatable(t))
end
No existe una función de copia suficientemente aceptada para ser llamada "estándar".
Una versión opcionalmente profunda, gráfica general, recursiva:
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
¿Quizás la copia metatable debería ser opcional también?
Use la biblioteca de penlight aquí: https://stevedonovan.github.io/Penlight/api/libraries/pl.tablex.html#deepcopy
local pl = require ''pl.import_into''()
local newTable = pl.tablex.deepcopy(oldTable)