lua deepcopy выберите индекс / значения в новую таблицу, а затем распечатайте новую таблицу

#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. Это все хорошо, но я полностью теряюсь, когда дело доходит до его фактического применения. Извините, что я такой новичок, но не мог бы кто-нибудь продемонстрировать, как применить их к проблеме, пожалуйста? Я продолжаю получать ошибки, и я действительно не понимаю, что я делаю. В текстовых комментариях мы были бы очень признательны!