ゲームを作っていると、テーブルをコピーしたい時があります(私にはありました)。
そもそも、テーブルのコピーは、「A = B」のように名前をイコールで繋げても出来ません。
次のPICO-8のコンソールでの実験結果を見てください。

まず、テーブルAには1と2を、テーブルBには3と4をリストとして、入れておきます。
テーブルBの内容をテーブルAにコピーしようとして、「A = B」を実行します。
テーブルAには、テーブルBの3と4がコピーされたかのようですが、テーブルBの1番目に5を代入すると、テーブルAの1番目も3ではなく、5になってしまいます。
つまり、これはアドレスをコピーして、実際のデータを指す場所をコピーしたので、テーブルAとテーブルBは、同じテーブルを指しているのです。
なので、「内容自体を複製したい」場合は、テーブルの要素(フィールドと言うらしい)を読み取って書き込まなくてはならないのです(ループ構文必須)。
現在制作中のゲーム「Magic Line」では、線で囲んだ部分の情報を得るためにテーブルのコピーを使っていました。
こんな感じです。
function t_copy(tag_t,src_t)
for x=1,31 do
for y=1,31 do
tag_t[x][y]=src_t[x][y]
end
end
end
第1引数にコピー先のテーブル、第2引数にコピー元のテーブルを渡して、XとYの二重ループで一つ一つ読み取り写します。
最近、アイテムの所持チェックなどを行うために、元の所持リストからコピーしてテンポラリリストとして更新に使い、ステージをクリアしたら、テンポラリリストを元の所持リストへコピーするように考えました。
ただ、今回は「アイテム名=bool値(真偽)」の辞書のテーブルとなるため、
「(先で紹介した)テーブルのコピー関数を使うわけにはいかない、また別に専用のコピー関数を作って実装するしか無いか、だとすると トークンを余計に使ってしまうな」
と考えていました。
そんな時、最近お世話になりまくりのチャッピーこと、ChatGPT!
質問したら、素敵なコードを吐き出してくれました。
function deepcopy(t)
local r = {}
for k,v in pairs(t) do
if type(v)=="table" then
r[k] = deepcopy(v) -- 再帰コピー
else
r[k] = v
end
end
return r
end
これです。
これだと、リストタイプ、辞書タイプ、混合でも良いし、テーブルの中のフィールドがテーブルにも対応しています(マルチ)!
for文のpairsを使って、インデックスや辞書のキーを読み込む所や、if文でフィールドをtypeでテーブルか調べて、そうならば再帰で自分自身を呼び出しているところがミソです。
returnでコピーテーブルを返しているところは違いますが、コピー先で受け取ればいいだけなので、2つ作る必要のあった関数を1つで済ますことが出来ました。
コメント