关于 lua corountine 的 resume 和 yield 函数的困惑

Confusion about lua corountine's resume and yield function

我正在通过这个video tutorial学习lua,它有这段代码:

co = coroutine.create(function()
    for i=1,5 do
      print(coroutine.yield(i))
    end
  end)

print(coroutine.resume(co,1,2))
print(coroutine.resume(co,3,4))
print(coroutine.resume(co,5,6))
print(coroutine.resume(co,7,8))
print(coroutine.resume(co,9,10))
print(coroutine.resume(co,11,12))

输出是这样的:

true    1
3   4
true    2
5   6
true    3
7   8
true    4
9   10
true    5
11  12
true

但我不明白 yield 和 resume 是如何相互传递参数的,为什么 yield 不输出 resume 传递给它的第一个 1,2,有人可以解释一下吗?谢谢

普通 Lua 函数有一个入口(参数传入)和一个出口(return 值被传递):

local function f( a, b )
  print( "arguments", a, b )
  return "I'm", "done"
end

print( "f returned", f( 1, 2 ) )
--> arguments       1       2
--> f returned      I'm     done

参数通过将参数名称(局部变量)放在括号内来绑定到参数名称(局部变量),作为 return 语句的一部分列出的 return 值可以通过调用函数来检索赋值语句右侧或较大表达式内部的表达式(例如,另一个函数调用)。

有多种调用函数的方法。例如。 pcall() 调用函数并捕获内部可能引发的任何运行时错误。通过将参数作为参数放入 pcall() 函数调用(就在函数本身之后)来传递参数。 pcall() 还附加了一个额外的 return 值,指示函数是正常退出还是通过错误退出。被调用函数内部不变

print( "f returned", pcall( f, 1, 2 ) )
--> arguments       1       2
--> f returned      true    I'm     done

您可以使用 coroutine.resume() 而不是 pcall() 来调用协程的主函数。传递参数的方式和额外的 return 值保持不变:

local th = coroutine.create( f )
print( "f returns", coroutine.resume( th, 1, 2 ) )
--> arguments       1       2
--> f returns       true    I'm     done

但是使用协同程序,您可以获得另一种(暂时)退出函数的方法:coroutine.yield()。您可以通过 coroutine.yield() 传递值,方法是将它们作为参数放入 yield() 函数调用中。这些值可以作为 coroutine.resume() 调用的 return 值而不是正常的 return 值在外部检索。

但是,您可以通过再次调用 coroutine.resume() 重新进入生成的协程。协程从它停止的地方继续,传递给 coroutine.resume() 的额外值可用作之前暂停协程的 yield() 函数调用的 return 值。

local function g( a, b )
  print( "arguments", a, b )
  local c, d = coroutine.yield( "a" )
  print( "yield returned", c, d )
  return "I'm", "done"
end

local th = coroutine.create( g )
print( "g yielded", coroutine.resume( th, 1, 2 ) )
print( "g returned", coroutine.resume( th, 3, 4 ) )
--> arguments       1       2
--> g yielded       true    a
--> yield returned  3       4
--> g returned      true    I'm     done

注意yield不需要直接在协程的main函数中,可以在嵌套的函数调用中。执行跳回到coroutine.resume(),它首先(重新)启动了协程。

现在回答你的问题,为什么第一个 resume() 中的 1, 2 没有出现在你的输出中:你的协程主函数没有列出任何参数,因此忽略了所有传递的参数到它(在第一个函数入口)。同样,由于您的 main 函数没有 return 任何 return 值,因此最后一个 resume() 没有 return 除了true 也表示执行成功。

co = coroutine.create(function()
    for i=1,5 do
      print(coroutine.yield(i))
    end
  end)

我们第一次启动协程使用:

print(coroutine.resume(co,1,2))

它将 运行 直到第一次屈服。我们的第一个 resume 调用将 return true 和解释第一行输出的 yield 参数(这里 i = 1)。

我们的协同程序现已挂起。一旦我们第二次调用简历:

print(coroutine.resume(co,3,4))

你的第一个 yield finally returns 和你当前简历的参数 (3,4) 将被打印出来。 for 循环第二次迭代开始,调用 coroutine.yield(2),暂停协程,这将再次使你的最后一个简历 return 为真,2 等等

所以实际上在你的例子中 coroutine.resume(co) 对于第一次调用就足够了,因为无论如何都会丢失任何进一步的参数。

我们看到这种行为的原因很微妙,但它与 "entering" 和 "exiting" yield 语句不匹配有关。它还与在匿名函数中调用 printyield 的顺序有关。

让我们想象一下 print(coroutine.yield(i)) 与迭代的执行图。

在第一次迭代中,我们 coroutine.resume12 传递给协程。这是原点,所以我们不是yield接取,而是匿名函数的原始调用本身。 yield 在打印中被调用,returning i=1print 未被调用。函数退出。

接下来是功能暂停一段时间,然后我们会看到下一个 coroutine.resume 恢复。此 resume 通过 34。函数 在最后 yield 开始。还记得 print 函数没有被调用,因为第一个 yield 函数首先被调用并退出了程序吗?好吧,在 print 中执行 returns,所以 print 现在被调用,但是这次 returning 34 作为这些是要传输的最新值。该函数再次重复,在 print 之前调用 yield,returning i=2

如果我们继续迭代,我们将更明确地了解为什么我们没有看到 12 的潜在模式。我们的第一次迭代是 "exiting" yield 与对应的 "entering yield." 不成对 这对应于协程的第一次执行 co.

我们可能希望最后一个 yield 也未配对,但不同之处在于我们将有一个 "entering" yield 未配对,而不是 "exiting" yield 未配对。这是因为循环已经结束了。这解释了为什么我们看到 11 12 后面跟着 true 后面没有 "exiting" yield return.

这种情况与里面for循环的奇偶性(even/oddness)无关。唯一重要的是 resumeyield 对以及它们的处理方式。您必须明白 yield 不会 return 第一次调用 resume 时函数内的值,它首先用于调用协程内的函数。