Lua,自定义迭代器 - 定义的正确方式?

我正在处理很多以 Lua 编写的数据文件。其中大部分以这种方式编写,以“电话簿”为例:

data = {
    -- 第一级 - 国家
    美国 = {
        -- 第二级 - 城市
        丹佛 = {
            -- 第三级 - 实际条目
            {姓名 = '约翰', 号码 = '12345'},
            -- 更多条目
        },
        华盛顿 = {
            {姓名 = '安', 号码 = '54321'},
            -- 更多条目
        },
        -- 具有条目的更多城市
    },
    -- 具有城市和条目的更多国家
}

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

现在,当实际搜索某些数据时,我想按条目包括此级别,隐含信息迭代此数据。

-- 协程产生包括级别数据的条目
function corIter(data)
    for country,l1 in pairs(data) do
        for city,l2 in pairs(l1) do
            for _,entry in pairs(l2) do
                -- 复制条目
                local out = {}
                for k,v in pairs(entry) do
                    out[k] = v
                end
                -- 添加级别属性
                out.country = country
                out.city = city
                coroutine.yield(out)
            end
        end
    end
end

-- 迭代条目
local cor = coroutine.create(corIter)
local _, entry = coroutine.resume(cor, data)
while entry do
    -- 处理条目,带有 'name'、'number'、'country' 和 'city' 键
    table.print(entry) -- (我使用的自定义打印函数)

    -- 获取下一个
    _, entry = coroutine.resume(cor)
end

但我认为这种方法可能很糟糕,因为它只是为了以特定方式迭代一个该死的表而保持整个线程活动。

是否有任何其他“明显”的解决方案?关键在于性能和易用性。我不需要一个通用解决方案(针对数据表内任意数量的“级别”),但总体而言,这都像是一个 hack。

点赞
用户1847592
用户1847592
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

-- 遍历 data 表中的电话信息
for country, town, abonent in phones(data) do
   print(country, town, abonent.name, abonent.number)
end
2017-09-01 20:32:09
用户8528277
用户8528277

你可以在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
                -- 拷贝条目
                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) -- 这是一个表
    for k,v in pairs(entry) do
        print(k,v) -- 这些是实际值
    end
end
2017-09-01 21:24:24