Lua协程作为迭代器:无法唤醒已经死亡的协程。

我修改了Lua 5.0在线文档中的"perm"示例:http://www.lua.org/pil/9.3.html。我的做法是将__call()元方法重新指向perm()函数。但它只能工作一次,并报告“无法恢复死协同程序”。有任何想法为什么它没能工作?

function permgen(a, n)
  if n == 0 then
    coroutine.yield(a)
  else
    for i = 1, n do
      -- 将第i个元素放在最后
      a[n], a[i] = a[i], a[n]

      -- 生成剩余元素的所有排列
      permgen(a, n - 1)

      -- 恢复第i个元素
      a[n], a[i] = a[i], a[n]
    end
  end
end

function perm(a)
  local n = table.getn(a)
  return coroutine.wrap(function() permgen(a, n) end)
end

K = {"a", "b", "c"}

for p in perm(K) do
  print(p[1], p[2], p[3])
end

for p in perm(K) do
  print(p[1], p[2], p[3])
end

-- 上面的所有内容都是从Lua在线文档中复制的,
-- 我的修改如下
setmetatable(K, { __call = perm(K) })

for p in K do
  print(p[1], p[2], p[3])
end

-- 无法重复!
-- perm.lua:44: cannot resume dead coroutine
for p in K do
  print(p[1], p[2], p[3])
end
点赞
用户1442917
用户1442917

因为你只调用了一次perm(K)并将结果分配给__call元方法,所以当你做in K时,你就使用了它一次,这完成了perm调用返回的协程的执行。当你尝试第二次这样做时,协程已经“死亡”,这触发了错误。

你需要做的是检测协程是否已经结束并重新创建它。由于你不能使用coroutine.wrap来完成这个操作,所以你需要使用一个稍微修改过的使用coroutine.create的解决方案。可能像这样的代码能够工作:

function perm (a)
  local n = table.getn(a)
  local co = coroutine.create(function () permgen(a, n) end)
  return function ()   -- iterator
    if coroutine.status(co) == 'dead' then co = coroutine.create(function () permgen(a, n) end) end
    local code, res = coroutine.resume(co)
    if not code then return nil end
    return res
  end
end

它在恢复协程之前检查协程的状态,如果它已经dead,那么它将使用相同的参数从头开始重新创建。

2015-12-17 05:25:40