Lua 迭代器使用迭代器

我定义了一个作为 C 绑定的函数,提供了一个通用的 for 迭代器:

for el in doc:each() do ... end

我想用 Lua 写一个迭代器,使用这个函数进行迭代,但要返回每个结果的修改。我该怎么做?


编辑:我确定我的迭代器必须从这里开始,但我在函数体中迷失了。

function myiterator()
   local f, c, v = doc:each()
   return (function(c2, v2)
      -- ??
   end), ??, ??
end
点赞
用户1944004
用户1944004

你可以将迭代器包装成一个协程,参见https://www.lua.org/pil/9.3.html

-- dummy object
local doc = {
    each = function()
        return pairs{ 11, 22, 33 }
    end
}

local myiterator = coroutine.wrap(function()
    local f, c, v = doc:each()
    return f, c, v
end)

for f, c, v in myiterator() do
    print(f, c, v)
end
$ lua test.lua
1   11  nil
2   22  nil
3   33  nil
2019-08-19 02:25:37
用户9383219
用户9383219

我不知道你的 C 函数是如何工作的,但是这里有一个迭代器函数,可以实现你所需的功能。它反复调用迭代器三元组,取第一个返回值,并调用一个函数返回一个新值:

function map(transformer, f, c, v)
    return function()
        v = f(c, v)
        if v ~= nil then
            return transformer(v)
        end
    end
end

例如,这个函数将取出表 { 'a', 'b', 'c' } 中的键,并将其平方。第二个返回值(与键对应的值)被忽略:

for v in map(function (x) return x * x end, pairs { 'a', 'b', 'c' }) do
    print(v)
end

你可以使用 map(function (elem) return do_something_to(elem) end, doc:each())

使用一个协程来编写 map 函数比较容易阅读:

function map(transformer, f, c, v)
    return coroutine.wrap(function ()
        for val in f, c, v do
            coroutine.yield(transformer(val))
        end
    end)
end

为了完整起见,这两个函数都可以使用原始迭代器三元组的两个返回值:

function map2(transformer, f, c, v)
    return function()
        local v2
        v, v2 = f(c, v)
        if v ~= nil then
            return transformer(v, v2)
        end
    end
end

function map(transformer, f, c, v)
    return coroutine.wrap(function ()
        for v, v2 in f, c, v do
            coroutine.yield(transformer(v, v2))
        end
    end)
end

for v in map2(function (a, b) return a .. b end, pairs { 'a', 'b', 'c' }) do
    print(v)
end
-- 这将打印出:
-- 1a
-- 2b
-- 3c
2019-08-19 05:39:10
用户6834680
用户6834680
function myiterator()
   local generator, state, prev_x = doc:each()

   local function my_generator()
      local x, y = generator(state, prev_x)
      if x ~= nil then
         prev_x = x
         -- modify x, y
         local modified_x = x + 100
         local modified_y = "("..y..")"
         -- modified_x must be non-nil
         return modified_x, modified_y
      end
   end

   return my_generator
end

Before:

local doc = {each = function() return ipairs{"aa", "bb", "cc"} end}
for x, y in doc:each() do
   print(x, y)
end

Output:

1   aa
2   bb
3   cc

After:

local doc = {each = function() return ipairs{"aa", "bb", "cc"} end}

-- 在此处插入 myiterator 的定义

for x, y in myiterator() do
   print(x, y) -- 现在 x 和 y 已被修改
end

Output:

101 (aa)
102 (bb)
103 (cc)
2019-08-19 10:00:54
用户405017
用户405017

感谢那些提供帮助的人。我学到了我的迭代器函数不一定需要返回2或3个值。这是一个有效的列表迭代器:

function every(list)
   local i=0
   return function() -- 没有使用参数!
      i = i+1
      return list[i]
   end
end

for word in every{'foo', 'bar', 'jim'} do print(word) end
--> foo
--> bar
--> jim

因为every()不返回第二个值(“不变”值),所以在每次迭代中作为第一参数传递了一个nil到匿名函数中。传递给匿名函数的第二个值是从函数在上一次调用返回的值(或第一次执行every()的第三个返回值)……但是如果这个值不帮助我们迭代,我们就不需要使用它。

因为迄今为止没有一个答案完全回答了我需要的问题,这里是我最终使用的解决方案:

function myiterator()
   local f, c, v = doc:each()
   return function()
      v = f(c,v)
      if v then
         -- 注意:不要在这里更改`v`变量,因为
         -- f()可能会期望它恰好是下一个迭代器在下一次调用时返回的值
         local myvalue = mutate(v)
         return myvalue
      end
   end
end
2019-08-19 15:08:29