在Lua中使用不同参数重复使用协程。

我发现重复利用一个已创建的coroutine非常有用。我找到了一个解决方法,如下所示:

co = coroutine.create(function (f, args)
  while f do
    f = coroutine.yield(f(args))
  end
end)

function dummyFunc(data)
  print("XXX "..data)
  coroutine.yield()
  print("OOO "..data)
end

coroutine.resume(co, dummyFunc, "1")
coroutine.resume(co, dummyFunc, "2")
coroutine.resume(co, dummyFunc, "3")
coroutine.resume(co, dummyFunc, "4")

这个方法很好用,但输出不是:

XXX 1
OOO 2
XXX 3
OOO 4

而是:

XXX 1
OOO 1
XXX 1
OOO 1

那么,在恢复调用之间更改dummyFunc的参数是可能的吗?

点赞
用户734069
用户734069

想一想。协同程序的工作方式如下。当你第一次 resume 时,你传递给 resume 的参数成为协同程序函数的参数。当协程 yield 时,它传递给 yield 的参数成为你的 resume 调用的返回值。

然而,第二次resume 协同程序时,它不会进入仍在执行的函数并更改第一次传入的参数。更改函数内部局部变量的值是极其粗鲁的行为。

因此,在第一次调用的 之后resume 的参数将是 yield返回值

co = coroutine.create(function (f, args)
  while f do
    f = coroutine.yield(f(args))
  end
end)

所以你需要这样做:

co = coroutine.create(function (f, args)
  while f do
    f, args = coroutine.yield(f(args))
  end
end)

但是,如果你想要更灵活的东西,可以接受可变数量的参数,那么你需要更聪明:

co = coroutine.create(function (...)
  local function capture_args(...)
    return {...}, select("#", ...)
  end

  local tbl, len = capture_args(...)
  local f = tbl[1]

  while f do
    tbl, len = capture_args(coroutine.yield(f(unpack(tbl, 2, len))
    f = tbl[1]
  end
end)

有些人不会使用 capture_args,仅依赖于 {...} 并对其调用 unpack。这是更安全的,因为用户可以在参数列表中放置 nil 值。'...' 将记录所有参数,即使是嵌入的 nil (而不是尾随的)。然而,一旦把它放到数组中,数组的长度就基于第一个 nil 值。

使用 capture_args,你可以通过 select 的一个被忽视的特性来获得 实际 的参数计数。而且由于 unpack 能够处理给定范围,即使范围超过了表的长度,你也可以有效地存储参数列表。

我可能可以通过在返回的表中放置长度来使 capture_args 更加聪明。但是对我来说,这已经足够好了。


这里有第二个问题:在 dummyFunc 中进行 yield,而 dummyFunc 似乎不知道如何处理 yield 的返回值(即你下一个 resume 调用的参数)。

不清楚你想让 dummyFunc 如何回应。如果你希望 dummyFunc 的参数由于你 不告诉 它而改变,那是不会发生的。

2015-12-08 18:35:58