Как к фактической копии переменной в Lua? - PullRequest
0 голосов
/ 31 декабря 2018

Итак, все следующие переменные ссылаются на одну и ту же таблицу:

x = {1,2,3}
y=x
z=y
table.remove(z,3)

Поэтому следующий код выведет 1,2

for k,v in pairs(x) do
    print(v)
end

Интернет просто относится к способности Luaвсегда использовать переменные по ссылке, а не по значению.

Но иногда я хочу манипулировать копией переменной, а не оригиналом.Как это сделать?Почему Lua так трудно действительно копировать переменную по значению, а не только по ссылке?

Ответы [ 2 ]

0 голосов
/ 01 января 2019

Число или строку можно скопировать, просто присвоив значение новой переменной.Стол с другой стороны потребует больше работы.

Чтобы скопировать таблицу в lua, вам нужно определить функцию копирования.2 распространенных типа функций копирования: мелкая копия и глубокая копия.

Пользователи Lua: CopyTable

Малая копия :

Это простая, наивная реализация.Он копирует только значение верхнего уровня и его прямых потомков;нет обработки более глубоких потомков, метатаблиц или специальных типов, таких как пользовательские данные или сопрограммы.Он также подвержен влиянию метаметода __pairs.

function shallowcopy(orig)
    local orig_type = type(orig)
    local copy
    if orig_type == 'table' then
        copy = {}
        for orig_key, orig_value in pairs(orig) do
            copy[orig_key] = orig_value
        end
    else -- number, string, boolean, etc
        copy = orig
    end
    return copy
end

Глубокая копия :

Глубокая копия копирует все уровни (иликонкретное подмножество уровней).Вот простая рекурсивная реализация, которая дополнительно обрабатывает метатаблицы и избегает метаметода __pairs.

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

Как сказал Никол Болас, существует множество ловушек при копировании таблицы.В другом вопросе SO как вы копируете таблицу-по-значению был приведен нижеприведенный пример, который охватывает некоторые из проблемных случаев, таких как

  • таблицы в качестве ключей
  • сохранение метатаблиц
  • рекурсивные таблицы

Пример из Tyler:

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

Каждая из этих функций имеетразличные варианты использования, и если вы работаете с мелкими таблицами, такими как x = {1,2,3}, вы можете сделать что-то такое простое, как:

x = {1,2,3}
y = {table.unpack(x)}
0 голосов
/ 31 декабря 2018

Почему Lua так трудно действительно копировать переменную по значению, а не только по ссылке?

Потому что то, что означает «копировать» таблицу, очень сильно зависит от того, что в этой таблице.

Сначала немного номенклатуры.Вы не ссылаетесь на «переменную»;вы получаете ссылку на таблицу .«Переменная» - это просто держатель для таких вещей, как число, строка или ссылка на таблицу.

Поэтому, когда вы говорите «действительно скопируйте переменную», вы на самом деле имеете в виду «копировать»стол."И ... это не просто.

Рассмотрим эту таблицу:

local tbl = {x = 5, y = {20}}

Если вы хотите скопировать эту таблицу, вы хотите, чтобы в поле y новой таблицы была копиятаблицы, которую хранил старый?Или вы хотите, чтобы эта таблица была копией оригинала?

Ни один из ответов не является неправильным;какой из них вы хотите, полностью зависит от того, что вы хотите сделать.Но вы не можете слепо делать рекурсивные копии таблиц, потому что:

local tbl = {x = 5, y = {20}}
tbl._tbl = tbl

Эта таблица теперь хранит ссылку на себя .Попытка сделать слепую рекурсивную копию этой таблицы приведет к бесконечной рекурсии.Вы должны будете обнаружить, что таблица ссылается на себя, и, таким образом, новая таблица хранит ссылку на новую таблицу.И это становится еще более сложным:

local tbl = {x = 5, y = {20}}
tbl.z = tbl.y

Эта таблица теперь имеет два поля, которые ссылаются на одну и ту же таблицу.Если вы хотите иметь истинную копию этой таблицы, то копия должна понимать, что два поля ссылаются друг на друга, поэтому при копировании первого поля второе поле может ссылаться на новую копию, а не копировать ее снова.

И я даже не разбирался в метатаблицах и в гимнастике, с которой вы можете заниматься.Также это не включает обсуждение вещей, которые принципиально не копируются, как объекты пользовательских данных из API на основе Си.Если я сохраню результат io.open в таблице, не будет механизма для копирования этого дескриптора файла.Итак, что должна делать ваша подпрограмма копирования?

Lua не имеет API-интерфейса копирования таблиц по умолчанию, чтобы убедиться, что вы сами потратите время, чтобы выяснить, насколько сложным должен быть ваш алгоритм копирования.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...