Question

Récemment, j'ai écrit un peu de code Lua, quelque chose comme:

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

Évidemment, ce n’était pas ce que je voulais faire, car les variables contiennent des références à une table anonyme et non les valeurs de la table elles-mêmes dans Lua. Ceci est clairement défini dans la Programmation en Lua , mais je l'avais oublié.

La question est donc de savoir ce que je devrais écrire au lieu de copy = a pour obtenir une copie des valeurs dans a ?

Était-ce utile?

La solution

Pour jouer à un code-golf lisible, voici une version courte qui gère les cas difficiles standard:

  • tables en tant que clés,
  • en préservant les méta-tables, et
  • tables récursives.

Nous pouvons le faire en 7 lignes:

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

Il existe une brève description des opérations de copie profonde Lua dans cet essentiel .

Une autre référence utile est cette page wiki de Lua-users , qui comprend un exemple sur la façon d'éviter le . __pairs metamethod.

Autres conseils

La copie de table comporte de nombreuses définitions potentielles. Cela dépend si vous voulez une copie simple ou profonde, si vous voulez copier, partager ou ignorer les méta-tables, etc. Aucune mise en œuvre unique ne peut satisfaire tout le monde.

Une approche consiste simplement à créer une nouvelle table et à dupliquer toutes les paires clé / valeur:

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)

Notez que vous devez utiliser paires au lieu de ipairs , car ipairs n'itère que sur un sous-ensemble des clés de la table (c'est-à-dire des valeurs positives consécutives). clés entières commençant à un dans l'ordre croissant).

Juste pour illustrer ce point, mon table.copy personnel prête également attention aux méta-tables:

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

Il n’existe pas de fonction de copie suffisamment largement reconnue pour être appelée "standard".

La version complète de la copie en profondeur, qui gère les 3 situations:

  1. Référence circulaire du tableau
  2. Les clés qui sont aussi des tables
  3. Metatable

La version générale:

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

Ou la version de la table:

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

Basé sur les lua-users.org/wiki/CopyTable et Fonctions d'Alan Yates .

Une version récursive, graphique général général, éventuellement profonde:

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

Peut-être que la copie métatable devrait également être facultative?

Voici ce que j'ai réellement fait:

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

En tant que mentionne Doub , si vos clés de table n'augmentent pas de façon strictement monotone, elles doivent être paires pas ipairs .

J'ai également trouvé une fonction deepcopy plus robuste. :

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

Il gère les tables et les méta-tables en s’appelant récursivement ( qui constitue sa propre récompense ). L'un des astuces réside dans le fait que vous pouvez lui transmettre n'importe quelle valeur (table ou non) et qu'elle sera copiée correctement. Cependant, le coût est que cela pourrait potentiellement déborder de la pile. Donc, une fonction plus robuste et encore plus robuste (non récursive) pourrait être nécessaire.

Mais c'est exagéré dans le cas très simple de vouloir copier un tableau dans une autre variable.

Le projet stdlib (malheureusement légèrement documenté) comporte de nombreuses extensions intéressantes pour plusieurs bibliothèques. livré avec la distribution standard Lua. Parmi celles-ci figurent plusieurs variantes sur le thème de la copie et de la fusion de tables.

Cette bibliothèque est également incluse dans la distribution Lua pour Windows et devrait probablement faire partie de tout Boîte à outils des utilisateurs sérieux de Lua.

Lors de la mise en œuvre manuelle de telles opérations, vous devez vous assurer que les méta-tables sont traitées correctement. Pour les applications simples de type table-structure, vous n'avez probablement pas de méta-tables, et une simple boucle utilisant pairs () est une réponse acceptable. Mais si la table est utilisée sous forme d’arbre, contient des références circulaires ou contient des méta-tables, la situation devient plus complexe.

N'oubliez pas que les fonctions sont aussi des références, donc si vous souhaitez entièrement copier toutes les valeurs, vous devez également obtenir des fonctions distinctes. Cependant, la seule façon de copier une fonction que je connaisse consiste à utiliser loadstring (string.dump (func)) , qui, selon le manuel de référence de Lua, ne fonctionne pas pour les fonctions à valeurs supérieures.

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

Avertissement: la solution indiquée est INCORRECT !

.

Lorsque la table contient des tables, les références à ces tables seront toujours utilisées. Je cherche depuis deux heures une erreur que j'ai commise, alors que c'était à cause de l'utilisation du code ci-dessus.

Vous devez donc vérifier si la valeur est une table ou non. Si tel est le cas, vous devez appeler table.copy récursivement!

C’est la fonction table.copy appropriée:

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

Remarque: ceci peut également être incomplet lorsque la table contient des fonctions ou d'autres types spéciaux, mais il est possible que la plupart d'entre nous n'aient pas besoin de cela. Le code ci-dessus est facilement adaptable à ceux qui en ont besoin.

C’est aussi bon que vous obtiendrez pour les tables de base. Utilisez quelque chose comme deepcopy si vous devez copier des tables avec des méta-tables.

Je pense que la raison pour laquelle Lua n'a pas 'table.copy ()' dans ses bibliothèques standard est parce que la tâche n'est pas précise à définir. Comme indiqué ci-dessus, on peut soit faire une copie "un niveau de profondeur" (ce que vous avez fait), une copie profonde avec ou sans se soucier d’éventuelles références en double. Et puis il y a des méta-tables.

Personnellement, je voudrais toujours qu’ils offrent une fonction intégrée. Seulement si les gens ne sont pas satisfaits de sa sémantique, ils devront aller le faire eux-mêmes. Pas très souvent, cependant, on a en fait le besoin de copie par valeur.

Dans la plupart des cas où je devais copier un tableau, je souhaitais avoir une copie qui ne partage rien avec l'original, de sorte que toute modification du tableau d'origine n'a aucun impact sur la copie (et inversement ).

Tous les extraits qui ont été affichés jusqu'à présent ne parviennent pas à créer une copie pour une table qui peut avoir des clés partagées ou des clés avec des tables car celles-ci seront dirigées vers la table d'origine. Il est facile de voir si vous essayez de copier une table créée en tant que: a = {}; a [a] = a . La fonction deepcopy référencée par Jon s'en charge, donc si vous devez créer une copie réelle / complète , deepcopy doit être utilisé.

Utilisez la bibliothèque de penlight ici: https://stevedonovan.github.io/Penlight/api/libraries /pl.tablex.html#deepcopy

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

Cela pourrait être la méthode la plus 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"

Dans mon cas, lorsque la table contient uniquement des données et d’autres tables (à l’exclusion des fonctions, ...), la ligne de code suivante constitue la solution gagnante:

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

J'écris du code Lua pour une domotique sur un Fibaro Home Center 2. L'implémentation de Lua est très limitée, sans bibliothèque centrale de fonctions à laquelle vous pouvez vous référer. Chaque fonction doit être déclarée dans le code pour que le code reste utilisable. Des solutions à une ligne comme celle-ci sont donc favorables.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top