最近、次のようなLuaコードを少し書きました。
local a = {}
for i = 1, n do
local copy = a
-- alter the values in the copy
end
明らかに、変数は匿名テーブルへの参照を保持しているため、Luaのテーブル自体の値ではなく、私がやりたいことではありませんでした。これは Luaのプログラミング で明確にレイアウトされていますが、私はそれを忘れていました。
それで、質問はcopy = a
の代わりに何を書いて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
this Gist にはLuaのディープコピー操作の簡単な説明があります。
別の便利なリファレンスは この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)
pairs
はテーブルキーのサブセット(つまり、1から順に増加する連続した正の整数キー)のみを反復するため、ipairs
の代わりにipairs
を使用する必要があることに注意してください。 。
ポイントを説明するために、私の個人的な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つの状況すべてを処理するディープコピーのフルバージョン:
一般版:
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
ではなく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
警告:マークされたソリューションは[〜#〜] incorrect [〜#〜]!です。
テーブルにテーブルが含まれる場合、それらのテーブルへの参照が代わりに使用されます。上記のコードを使用したためでしたが、私は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レベルの深さ」のコピーを作成することができます(これはあなたが行いました)。そして、メタテーブルがあります。
個人的には、組み込み機能を提供してほしいです。人々がそのセマンティクスに満足しない場合にのみ、彼らは自分でそれを行う必要があります。ただし、それほど頻繁ではありませんが、実際には値ごとのコピーが必要です。
ここでペンライトライブラリを使用します: https://stevedonovan.github.io/Penlight/api/libraries/pl.tablex.html#deepcopy
local pl = require 'pl.import_into'()
local newTable = pl.tablex.deepcopy(oldTable)
テーブルをコピーする必要があるほとんどの場合、元のテーブルの変更がコピーに影響を与えないように(およびその逆も)、オリジナルと何も共有しないコピーが必要でした。
これまでに示されたすべてのスニペットは、共有キーまたはテーブルとのキーが元のテーブルを指しているため、それらのキーのコピーを作成できません。 a = {}; a[a] = a
として作成されたテーブルをコピーしようとしても簡単にわかります。 deepcopy Jonが参照する関数がそれを処理するため、実/完全コピーを作成する必要がある場合は、deepcopy
を使用する必要があります。
これは最も簡単な方法かもしれません:
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行のソリューションが適しています。