값으로 Lua 테이블을 어떻게 복사합니까?

lottogame 2021. 1. 6. 07:38

값으로 Lua 테이블을 어떻게 복사합니까?

최근에 다음과 같은 루아 코드를 작성했습니다.

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

분명히 그것은 변수가 Lua에서 테이블 자체의 값이 아닌 익명 테이블에 대한 참조를 보유하기 때문에 내가하고 싶었던 것이 아닙니다. 이것은 Programming in 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

이 요점 에는 Lua 딥 카피 작업에 대한 짧은 글 있습니다.

또 다른 유용한 참조는 메타 메서드 를 피하는 방법에 대한 예제를 포함하는 이 Lua-users 위키 페이지__pairs 입니다.

테이블 복사에는 많은 잠재적 정의가 있습니다. 단순 복사를 원하는지 깊은 복사를 원하는지, 메타 테이블을 복사, 공유 또는 무시할지 등에 따라 다릅니다. 모두를 만족시킬 수있는 단일 구현은 없습니다.

한 가지 방법은 간단히 새 테이블을 만들고 모든 키 / 값 쌍을 복제하는 것입니다.

function table.shallow_copy(t)
  local t2 = {}
  for k,v in pairs(t) do
    t2[k] = v
  return t2

copy = table.shallow_copy(a)

당신이 사용하도록주의 pairs대신에 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))

"표준"이라고 할만큼 널리 합의 된 복사 기능은 없습니다.

세 가지 상황을 모두 처리하는 전체 버전의 딥 카피 :

  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)
    setmetatable(no, deepcopy(getmetatable(o), seen))
  else -- number, string, boolean, etc
    no = o
  return no

또는 테이블 버전 :

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
  return no
end 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)
            nt[k] = v
    setmetatable(nt, table.copy(getmetatable(t), deep, seen))
    seen[t] = nt
    return nt

메타 테이블 복사본도 선택 사항이어야합니까?

내가 실제로 한 일은 다음과 같습니다.

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)
        setmetatable(copy, deepcopy(getmetatable(orig)))
    else -- number, string, boolean, etc
        copy = orig
    return copy

It handles tables and metatables by calling itself recursively (which is its own reward). One of the clever bits is that you can pass it any value (whether a table or not) and it will be copied correctly. However, the cost is that it could potentially overflow the stack. So and even more robust (non-recursive) function might be needed.

But that's overkill for the very simple case of wanting to copy an array into another variable.

The (unfortunately lightly documented) stdlib project has a number of valuable extensions to several of the libraries shipped with the standard Lua distribution. Among them are several variations on the theme of table copying and merging.

This library is also included in the Lua for Windows distribution, and should probably be a part of any serious Lua user's toolbox.

One thing to make sure of when implementing things like this by hand is the proper handling of metatables. For simple table-as-structure applications you probably don't have any metatables, and a simple loop using pairs() is an acceptable answer. But if the table is used as a tree, or contains circular references, or has metatables, then things get more complex.

Don't forget that functions are also references, so if you wanted to completely 'copy' all of the values you'd need to get separate functions, too; however, the only way I know to copy a function is to use loadstring(string.dump(func)), which according to the Lua reference manual, doesn't work for functions with upvalues.

    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)
                new_value = value
            new_tbl[key] = new_value
        return new_tbl
    table.copy = table_copy

Warning: the marked solution is INCORRECT!

When the table contains tables, references to those tables will still be used instead. I have been searching two hours for a mistake that I was making, while it was because of using the above code.

So you need to check if the value is a table or not. If it is, you should call table.copy recursively!

This is the correct table.copy function:

function table.copy(t)
  local t2 = {};
  for k,v in pairs(t) do
    if type(v) == "table" then
        t2[k] = table.copy(v);
        t2[k] = v;
  return t2;

Note: This might also be incomplete when the table contains functions or other special types, but that is possible something most of us don't need. The above code is easily adaptable for those who need it.

That's as good as you'll get for basic tables. Use something like deepcopy if you need to copy tables with metatables.

I think the reason why Lua doesn't have 'table.copy()' in its standard libraries is because the task is not precise to define. As shown already here, one can either make a copy "one level deep" (which you did), a deepcopy with or without caring of possible duplicate references. And then there's metatables.

Personally, I would still like them to offer a built-in function. Only if people wouldn't be pleased with its semantics, they would need to go do it themselves. Not very often, though, one actually has the copy-by-value need.

In most of the cases when I needed to copy a table, I wanted to have a copy that doesn't share anything with the original, such that any modification of the original table has no impact on the copy (and vice versa).

All the snippets that have been shown so far fail at creating a copy for a table that may have shared keys or keys with tables as those are going to be left pointing to the original table. It's easy to see if you try to copy a table created as: a = {}; a[a] = a. deepcopy function referenced by Jon takes care of that, so if you need to create a real/full copy, deepcopy should be used.

Use penlight library here:

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

This might be the simplest method:

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
    return newtable

new_table = table.copy(data)  --copys the table "data"

In my situation, when the information in the table is only data and other tables (excluding functions, ...), is the following line of code the winning solution:

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

I'm writing Lua code for some home automation on a Fibaro Home Center 2. The implementation of Lua is very limited with no central library of functions you can refer to. Every function needs to be declared in the code so to keep the code serviceable, so one line solutions like this are favorable.

ReferenceURL :