Luaテーブルを値でコピーするにはどうすればよいですか?

StackOverflow https://stackoverflow.com/questions/640642

  •  22-07-2019
  •  | 
  •  

質問

最近、次のようなLuaコードを少し書きました:

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

明らかに、変数は、Luaのテーブル自体の値ではなく、匿名テーブルへの参照を保持しているため、私がやりたいことではありませんでした。これは Luaでのプログラミングで明確にレイアウトされていますが、私はそれを忘れていました。

では、 a の値のコピーを取得するには、 copy = a の代わりに何を書くべきですか?

役に立ちましたか?

解決

少し読みやすいコードゴルフをプレイするために、標準的なトリッキーなケースを処理する短いバージョンを以下に示します。

  • キーとしてのテーブル、
  • メタテーブルの保存、および
  • 再帰テーブル。

これは7行で行えます:

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

この要点には、Luaのディープコピー操作の簡単な説明があります。

もう1つの有用なリファレンスは、このLua-users wikiページです。これには、を回避する方法の例が含まれています__pairs メタメソッド。

他のヒント

テーブルコピーには多くの潜在的な定義があります。シンプルコピーとディープコピーのどちらを使用するか、メタテーブルをコピー、共有、または無視するかどうかなどに依存します。誰もが満足できる単一の実装はありません。

1つのアプローチは、新しいテーブルを作成し、すべてのキー/値ペアを複製することです:

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)

ipairs の代わりに pairs を使用する必要があることに注意してください。これは、 ipairs がテーブルキーのサブセットのみを反復するためです(つまり、連続した正昇順で1から始まる整数キー)。

ポイントを説明するために、私の個人的な table.copy はメタテーブルにも注意を払っています:

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

「標準」と呼ばれるほど広く合意されたコピー機能はありません。

ディープコピーのフルバージョン。3つの状況すべてを処理します。

  1. テーブル循環参照
  2. 表でもあるキー
  3. メタテーブル

一般的なバージョン:

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

またはテーブルバージョン:

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

lua-users.org/wiki/CopyTable および Alan Yates 」の機能。

オプションで深みのあるグラフ全般の再帰バージョン:

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

おそらくメタテーブルのコピーもオプションにする必要がありますか?

実際にやったことは次のとおりです。

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

Doub言及のように、テーブルキーが厳密に単調増加していない場合は、 pairs でなければなりませんcode> ipairs ではありません。

より堅牢な deepcopy 関数も見つかりました:

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

テーブルとメタテーブルを再帰的に呼び出して処理します(独自の報酬)。巧妙なビットの1つは、(テーブルであるかどうかにかかわらず)任意の値を渡すことができ、正しくコピーされることです。ただし、コストはスタックをオーバーフローさせる可能性があることです。そのため、さらに堅牢な(非再帰的)関数が必要になる場合があります。

しかし、配列を別の変数にコピーしたいという非常に単純な場合には、それはやり過ぎです。

(残念ながら簡単に文書化されています) stdlib プロジェクトには、いくつかのライブラリに対する多くの貴重な拡張機能があります。標準のLuaディストリビューションに同梱されています。それらの中には、テーブルのコピーとマージのテーマに関するいくつかのバリエーションがあります。

このライブラリは、 Lua for Windows ディストリビューションにも含まれており、おそらくどのライブラリにも含まれているはずです。深刻なLuaユーザーのツールボックス。

このようなものを手作業で実装する際に確認すべきことの1つは、メタテーブルの適切な処理です。単純なテーブルとしての構造のアプリケーションの場合、おそらくメタテーブルはなく、 pairs()を使用した単純なループは受け入れられる答えです。ただし、テーブルがツリーとして使用されている場合、循環参照が含まれている場合、またはメタテーブルがある場合、事態はさらに複雑になります。

関数も参照であることを忘れないでください。したがって、すべての値を完全に「コピー」したい場合は、別個の関数も取得する必要があります。ただし、関数をコピーする唯一の方法は、 loadstring(string.dump(func))を使用することです。これは、Luaリファレンスマニュアルによると、アップバリューを持つ関数では機能しません。

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

警告:マークされたソリューションは間違っています

テーブルにテーブルが含まれる場合、それらのテーブルへの参照が代わりに使用されます。上記のコードを使用したためでしたが、私が犯していた間違いを2時間探していました。

したがって、値がテーブルかどうかを確認する必要があります。その場合、table.copyを再帰的に呼び出す必要があります!

これは正しい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

注:テーブルに関数または他の特殊な型が含まれる場合、これも不完全な場合がありますが、ほとんどの人が必要としない可能性があります。上記のコードは、それを必要とする人に簡単に適応できます。

これは、基本的なテーブルで得られるものと同じです。メタテーブルを含むテーブルをコピーする必要がある場合は、ディープコピーなどを使用します。

Luaの標準ライブラリに「table.copy()」がないのは、タスクの定義が正確ではないためだと思います。ここですでに示したように、「1レベルの深さ」のコピーを作成できます。 (あなたがした)、可能性のある重複参照の世話の有無にかかわらずディープコピー。そして、メタテーブルがあります。

個人的には、組み込み関数を提供してほしいです。人々がそのセマンティクスに満足しない場合にのみ、彼らは自分でそれを行う必要があります。ただし、それほど頻繁ではありませんが、実際には値ごとのコピーが必要です。

テーブルをコピーする必要があるほとんどの場合、元のテーブルの変更がコピーに影響を与えないように、元のテーブルとは何も共有しないコピーが必要でした(逆も同様です) )。

これまでに表示されたすべてのスニペットは、共有キーまたはテーブルとのキーが元のテーブルを指しているため、それらのテーブルのコピーの作成に失敗します。次のように作成されたテーブルをコピーしようとしても簡単にわかります。 a = {}; a [a] = a 。 Jonが参照する deepcopy 関数がそれを処理するため、実際のコピーまたは完全なコピーを作成する必要がある場合、 deepcopy を使用する必要があります。

ここでペンライトライブラリを使用します。 https://stevedonovan.github.io/Penlight/api/libraries /pl.tablex.html#deepcopy

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

これは最も簡単な方法かもしれません:

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"

私の状況では、テーブル内の情報がデータと他のテーブル(関数を除く...)のみである場合、次のコード行が解決策となります。

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

Fibaro Home Center 2でホームオートメーション用のLuaコードを書いています。Luaの実装は非常に限られており、参照できる関数の中央ライブラリはありません。すべての関数はコードで宣言する必要があるため、コードを保守しやすくするため、このような1行のソリューションが適しています。

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top