【问题标题】:Lua, custom iterator - proper way to define?Lua,自定义迭代器 - 定义的正确方法?
【发布时间】:2017-09-01 18:58:25
【问题描述】:

我正在处理大量用 Lua 编写的数据文件。大部分都是这样写的,以“电话簿”为例:

data = {
    -- First Level - country
    USA = {
        -- Second level - city
        Denver = {
            -- Third level - actual entries
            {name = 'John', number = '12345'},
            -- more entries
        },
        Washington = {
            {name = 'Ann', number = '54321'},
            -- more entries
        },
        -- more cities with entries
    },
    -- more countries with cities and entries
}

因此,第一级是“国家”,第二级是“城市”这一事实是隐含的,但它使数据更加紧凑。

现在,当实际搜索某些数据时,我想将这些数据作为条目进行迭代包括此分级的隐式信息

-- Coroutine yielding entries including level data
function corIter(data)
    for country,l1 in pairs(data) do
        for city,l2 in pairs(l1) do
            for _,entry in pairs(l2) do
                -- Copy the entry
                local out = {}
                for k,v in pairs(entry) do
                    out[k] = v
                end
                -- Add level properties
                out.country = country
                out.city = city
                coroutine.yield(out)
            end
        end
    end
end

-- Iterate over the entries
local cor = coroutine.create(corIter)
local _, entry = coroutine.resume(cor, data)
while entry do
    -- Handle the entry, has 'name', 'number', 'country' and 'city' keys
    table.print(entry) -- (custom print function I use)

    -- Get the next one
    _, entry = coroutine.resume(cor)
end  

但我认为这种方法可能不好,因为它使整个线程保持活动状态只是为了以特定方式迭代该死的表。

还有其他“明显”的解决方案吗?关键是性能和易用性。我并不完全需要一个通用的解决方案(对于数据表中任意数量的“级别”),但这一切都像黑客一样。

【问题讨论】:

  • 你能举一个你想到的查询的例子吗?

标签: lua iterator


【解决方案1】:

您可以在 Lua 中创建自己的自定义迭代器,无需使用协程。迭代器是在调用时返回结构中的下一个元素的函数(您可以使用任何所需的结构)。

您的示例的迭代器将是这样的:

function corIter(data)
    local itOut = {}

    for country,l1 in pairs(data) do
        for city,l2 in pairs(l1) do
            for _,entry in pairs(l2) do
                -- Copy the entry
                local out = {}
                for k,v in pairs(entry) do
                    out[k] = v
                end

                out.country = country
                out.city = city

                table.insert(itOut,out)
            end
        end
    end

    local i = 0
    return function()
        i = i + 1
        return itOut[i]
    end
end

“corIter”返回的匿名函数将返回数据中的下一个元素。请注意,当我们使用 'pairs' 将条目复制到另一个表以对其进行迭代时,无法保证条目的顺序将保持与原始顺序相同。

所以现在您可以使用此代码打印条目:

for entry in corIter(data) do
    print(entry) -- this is a table
    for k,v in pairs(entry) do
        print(k,v) -- these are the actual values
    end
end

【讨论】:

  • 非常感谢!如果我理解正确,这会将数据表的副本保留为迭代器函数的上值,对吗?这一切都非常清晰明了,很好:)
【解决方案2】:
local function phones(d)
   local cn, c, tn, t, i
   return
      function()
         local a
         repeat
            if tn then
               a, i = t[i], i+1
               if not a then
                  i, tn, t = 1, next(c, tn)
               end
            else
               cn, c = next(d, cn)
               i, tn, t = 1, next(c or {})
            end
         until a or not cn
         return cn, tn, a
      end
end

for country, town, abonent in phones(data) do
   print(country, town, abonent.name, abonent.number)
end

【讨论】:

  • 这可能比我喜欢的要紧凑一些,但我想我可以看到该方法的一般要点,谢谢。也喜欢多次返回,而不是像我一样将数据混合在一起。
猜你喜欢
  • 1970-01-01
  • 2020-04-21
  • 2013-02-17
  • 2015-02-04
  • 2019-12-06
  • 2021-02-13
  • 2011-04-04
  • 1970-01-01
相关资源
最近更新 更多