#lua
#lua
Вопрос:
новичок в lua, и мне нужна помощь в понимании процесса deepcopy из вики пользователей lua. По сути, у меня есть таблица in ipairs с несколькими атрибутами для каждого фрагмента индекса, и я хочу скопировать весь фрагмент индекса, где атрибуты соответствуют определенным условиям if, и запустить в цикле for. Наконец, я хочу распечатать содержимое новой таблицы.
Таблица очень длинная, поэтому я просто включу короткий отрывок.
local tblStudents = {
{
['Name'] = 'Jeff',
['Year'] = 'Twelve-A',
['Class'] = 'Ms Edwards',
['Attended'] = true
},
{
['Name'] = 'Tom',
['Year'] = 'Twelve-B',
['Class'] = 'Ms Edwards',
['Attended'] = true
},
{
['Name'] = 'Billy',
['Year'] = 'Twelve-A',
['Class'] = 'Ms Edwards',
['Attended'] = false
},
{
['Name'] = 'Jack',
['Year'] = 'Twelve-B',
['Class'] = 'Ms Edwards',
['Attended'] = false
},
{
['Name'] = 'Sam',
['Year'] = 'Twelve-A',
['Class'] = 'Mr Green',
['Attended'] = true
},
{
['Name'] = 'Diego',
['Year'] = 'Twelve-A',
['Class'] = 'Mr Green',
['Attended'] = false
},
{
['Name'] = 'Peta',
['Year'] = 'Twelve-A',
['Class'] = 'Mr Green',
['Attended'] = false
},
{
['Name'] = 'Will',
['Year'] = 'Twelve-A',
['Class'] = 'Ms Edwards',
['Attended'] = true
},
{
['Name'] = 'Sara',
['Year'] = 'Twelve-B',
['Class'] = 'Ms Edwards',
['Attended'] = true
},
{
['Name'] = 'Lisa',
['Year'] = 'Twelve-A',
['Class'] = 'Ms Edwards',
['Attended'] = true
}
}
Что я хочу сделать, так это скопировать всех учеников из Twelve-A, которые не обращали внимания на новую таблицу, tblTruant Я полагаю, что для этого в заявлении будет использоваться условное:
for i,v in ipairs (tblStudents) do
if v.Year == 'Twelve-A' and v.Attended == false then
--deepcopy to new table.
Демонстрационный код, который я получил для deepcopy, таков:
local function deepCopy(original)
local copy = {}
for k, v in pairs(original) do
if type(v) == "table" then
v = deepCopy(v)
end
copy[k] = v
end
return copy
end
Я не уверен, как применить это, используя условие, описанное выше.
Для печати результатов новой таблицы мне сказали, что я не могу распечатать таблицу в виде строки, использовать эту рекурсивную функцию:
function dump(o)
if type(o) == 'table' then
local s = '{ '
for k,v in pairs(o) do
if type(k) ~= 'number' then k = '"'..k..'"' end
s = s .. '['..k..'] = ' .. dump(v) .. ','
end
return s .. '} '
else
return tostring(o)
end
end
Я предполагаю, что это позволит мне использовать print(dump(tblTruant) для получения желаемого результата.
Любая помощь с благодарностью!
Ответ №1:
Вам не нужно делать намного больше, чем у вас уже есть. Просто создайте новую таблицу и вставьте в нее интересные элементы:
tblTruant = {} -- create a new empty table
for i,v in ipairs (tblStudents) do
if v.Year == 'Twelve-A' and v.Attended == false then
table.insert(tblTruant, deepCopy(v)) -- deepcopy to new table
print(dump(tblTruant)) -- print the new table contents
Любые возможные улучшения dump
или deepCopy
совсем другое дело.
Ответ №2:
Ваша deepcopy
функция нуждается в некотором улучшении:
- он не обрабатывает случай, когда
original
это не таблица, - поскольку
key
может быть таблицей (хотя и не в вашем примере), ее необходимо глубоко скопировать, - глубокое копирование предполагает копирование ссылки на метатаблицу, хотя вы ее не используете,
- есть еще одна проблема с
deepCopy
иdump
. Хотя это не имеет отношения к таблице в примерах, общие таблицы могут быть рекурсивными, то есть содержать ссылки на самих себя. С этим нужно разобраться. Возможный способ — сохранить «набор» таблиц, которые уже обрабатываются функциями. Этот набор может быть сохранен как таблица, локальная в той же области, что и функция, которую она обслуживает, но чтобы сделать ее «статической», т. Е. Доступной Из функции, Оба могут быть обернуты в функцию второго порядка.
Улучшенная версия:
local concat = table.concat
local function handleRecursiveTables (func, stub)
local calls = {}
return function (arg)
if type (arg) == 'table' and calls [arg] then
return stub (arg)
end
calls [arg] = true
return func (arg)
end
end
local function deepCopy (original)
local copy
if type (original) ~= 'table' then
copy = original
else
copy = {}
for key, value in pairs (original) do
copy [deepCopy (key)] = deepCopy (value)
end
end
if type (original) == 'table' or type (original) == 'userdata' then
setmetatable (copy, getmetatable (original))
end
return copy
end
deepCopy = handleRecursiveTables (deepCopy, function (original)
return original
end)
Назовите это так:
local truants = {}
for i,v in ipairs (tblStudents) do
if v.Year == 'Twelve-A' and v.Attended == false then
--deepcopy to new table.
truants [#truants 1] = deepCopy (v)
end
end
Аналогичные улучшения для dump
придания ему более общего характера:
local function dump (o)
if type (o) == 'string' then
return '"' .. o .. '"'
elseif type (o) == 'table' then
local items = {}
for key, value in pairs (o) do
items [#items 1] = '[' .. dump (key) .. '] = ' .. dump (value)
end
return '[' .. concat (items, ', ') .. ']'
else
return tostring (o)
end
end
dump = handleRecursiveTables (dump, function (o)
return '(recursive table reference)'
end)
UPD Полный рабочий пример:
-- Utility function to handle recursive tables. Not really necesary, bit demonstrates some principles.
-- A similar technique is used for caching expensive function calls.
local concat = table.concat -- localise table value to speed it up.
-- func is a function that takes one argument of the type table and needs to be enabled to handle recursive tables.
-- stub is the function that handles the table if the table has already been processed to stop infinite recursion.
local function handleRecursiveTables (func, stub)
local calls = {} -- a set of already processed tables.
return function (arg)
if type (arg) == 'table' then
if calls [arg] then
return stub (arg) -- stop recursion.
end
calls [arg] = true -- remember that arg has been processed.
end
return func (arg) -- actually call the function.
end
end
-- Naïve implementation of deep copy.
local function deepCopy (original)
local copy
if type (original) ~= 'table' then
copy = original -- not a table, so simply return the scalar value.
else
copy = {}
for key, value in pairs (original) do
-- We need to deep copy both the key and the value, as any can be a table.
copy [deepCopy (key)] = deepCopy (value)
end
end
-- Copy the reference to the metatable, if any.
if type (original) == 'table' or type (original) == 'userdata' then
setmetatable (copy, getmetatable (original))
end
return copy
end
-- Make the function aware of recursive tables.
-- Not that recursive calls of deepCopy will also be affected.
deepCopy = handleRecursiveTables (deepCopy, function (original)
return original
end)
-- Naïve implementation of dump.
local function dump (o)
if type (o) == 'string' then
return '"' .. o .. '"' -- quote the string, wherever it appears.
elseif type (o) == 'table' then
local items = {} -- '[key] = value' chunks.
for key, value in pairs (o) do
-- Use dumped key and value.
items [#items 1] = '[' .. dump (key) .. '] = ' .. dump (value)
end
-- table.concat once is faster than applying .. again and again.
-- Also, there will be no trailing comma.
return '[' .. concat (items, ', ') .. ']'
else
return tostring (o) -- numbers, functions, userdata.
end
end
-- Make the function aware of recursive tables.
-- Not that recursive calls of dump will also be affected.
dump = handleRecursiveTables (dump, function (o)
return '(recursive table reference)'
end)
-- This function filters out truants from the given year and returns a table of their deep copies.
local function getTruants (students, year)
local truants = {}
for _, student in ipairs (students) do -- we don't need the key, so use _ as the variable name.
if student.Year == year and student.Attended == false then
-- you can use 'not student.Attended' instead of 'student.Attended == false'
-- but in this case, if Attended has not been explicitly set, it will be falsy.
--deepcopy to new table.
truants [#truants 1] = deepCopy (student)
end
end
return truants
end
-- Test dataset:
local tblStudents = {
{
-- Note that Name = 'Jeff' is the same as ['Name'] = 'Jeff'.
-- 'Name' has to consist of underscores, letters (at least one of either) and numbers.
Name = 'Jeff',
Year = 'Twelve-A',
Class = 'Ms Edwards',
Attended = true
},
{
Name = 'Tom',
Year = 'Twelve-B',
Class = 'Ms Edwards',
Attended = true
},
{
Name = 'Billy',
Year = 'Twelve-A',
Class = 'Ms Edwards',
Attended = false
},
{
Name = 'Jack',
Year = 'Twelve-B',
Class = 'Ms Edwards',
Attended = false
},
{
Name = 'Sam',
Year = 'Twelve-A',
Class = 'Mr Green',
Attended = true
},
{
Name = 'Diego',
Year = 'Twelve-A',
Class = 'Mr Green',
Attended = false
},
{
Name = 'Peta',
Year = 'Twelve-A',
Class = 'Mr Green',
Attended = false
},
{
Name = 'Will',
Year = 'Twelve-A',
Class = 'Ms Edwards',
Attended = true
},
{
Name = 'Sara',
Year = 'Twelve-B',
Class = 'Ms Edwards',
Attended = true
},
{
Name = 'Lisa',
Year = 'Twelve-A',
Class = 'Ms Edwards',
Attended = true
}
}
-- The following line is an example of a self-reference in a table.
-- Without handleRecursiveTables it would cause infinite recursion.
tblStudents [1].allStudents = tblStudents
-- Print a list of truants.
print (dump (getTruants (tblStudents, 'Twelve-A')))
Комментарии:
1. «следовательно, рекурсия также не будет работать так, как ожидалось», не следует из «он не обрабатывает случай, когда оригинал не является таблицей;»
2. Как «v = deepCopy (v) изменит v на месте, т. Е. В оригинале».?
3. Таблицы являются ссылками, поэтому
original[k]
будут заменены их глубокой копией. По первому вопросу вы, вероятно, правы.4.Но это не меняется
original[k]
. Это изменение переменнойv
из ссылки в исходную на результат рекурсивного вызова.var = foo
нетvar.whatever = foo
.5. Это все хорошо, но я полностью теряюсь, когда дело доходит до его фактического применения. Извините, что я такой новичок, но не мог бы кто-нибудь продемонстрировать, как применить их к проблеме, пожалуйста? Я продолжаю получать ошибки, и я действительно не понимаю, что я делаю. В текстовых комментариях мы были бы очень признательны!