我正在通过这个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
但是我不明白收益和恢复如何将参数传递给对方以及为什么收益率不输出恢复传递给它的前1,2,有人可以解释一下吗?感谢
答案 0 :(得分:5)
普通Lua函数有一个条目(传入参数的地方)和一个exit(传递返回值):
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
语句一部分列出的返回值赋值语句的一侧,或大型表达式的一部分(例如另一个函数调用)。
还有其他方法可以调用函数。例如。 pcall()
调用一个函数并捕获可能在其中引发的任何运行时错误。通过将它们作为参数放入pcall()
函数调用(函数本身后面)来传入参数。 pcall()
还预先添加一个额外的返回值,指示函数是正常退出还是通过错误退出。被调用函数的内部没有变化。
print( "f returned", pcall( f, 1, 2 ) )
--> arguments 1 2
--> f returned true I'm done
您可以使用coroutine.resume()
代替pcall()
来调用协程的主要功能。参数的传递方式和额外的返回值保持不变:
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()
调用的返回值而不是正常返回值在外部检索。
但是,您可以再次调用coroutine.resume()
来重新输入产生的协程。协程从中断处继续,并且传递给coroutine.resume()
的额外值可用作之前暂停协程的yield()
函数调用的返回值。
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不需要直接在coroutine的main函数中,它可以在嵌套函数调用中。执行跳转回coroutine.resume()
,首先(重新)启动协程。
现在问你为什么第一个1, 2
中的resume()
没有出现在你的输出中:你的coroutine main函数没有列出任何参数,所以忽略了传递给它的所有参数(在第一个功能输入上)。在类似的说明中,由于您的main函数没有返回任何返回值,因此最后resume()
除了表示成功执行的true
之外不会返回任何额外的返回值。
答案 1 :(得分:2)
co = coroutine.create(function()
for i=1,5 do
print(coroutine.yield(i))
end
end)
我们首次使用以下方式启动协程:
print(coroutine.resume(co,1,2))
它会一直运行到第一次产量。我们的第一个简历调用将返回true,并且yield的参数(此处为i = 1)解释了第一个输出行。
我们的协程现已暂停。一旦我们第二次打电话给简历:
print(coroutine.resume(co,3,4))
您的第一个收益率最终返回,并且将打印当前简历(3,4)的参数。 for循环第二次迭代开始,调用coroutine.yield(2),继续使用coroutine,这将使你的最后一个简历返回true,等等
所以实际上在你的例子中,coroutine.resume(co)对于第一次调用就足够了,因为任何进一步的参数都会丢失。
答案 2 :(得分:0)
我们看到此行为的原因很微妙,但这与“输入”和“退出” yield
语句的不匹配有关。这也与在匿名函数中调用print
和yield
的顺序有关。
让我们想象一下print(coroutine.yield(i))
与迭代执行的关系图。
在第一次迭代中,我们将coroutine.resume
和1
传递给协程。这是起点,因此我们不是从以前的 2
那里接手,而是从匿名函数本身的原始调用中接管。 yield
在打印中被调用,返回yield
,但未调用i=1
。该函数退出。
接下来是函数暂停一段时间,直到我们看到下一个print
恢复。此coroutine.resume
传递了resume
和3
。函数最后出现 4
。还记得第一次调用第一个yield
并退出程序时未调用print
函数吗?好吧,执行返回到yield
内部,因此现在调用print
,但是这次返回print
和3
,因为它们是要转移的最新值。该函数再次重复,在4
之前调用yield
,返回print
。
如果我们进行迭代,那么我们将更明确地确定为什么看不到i=2
和1
的潜在原因。我们的第一个迭代是“退出”收益率与相应的“进入收益率”不成对。这对应于协程2
的第一次执行。
我们可能希望最后一个co
也会不成对,但是不同之处在于,我们将有一个未配对的“进入” yield
,而不是“退出” {{1} }未配对。这是因为循环已经完成。这解释了为什么我们看到yield
之后是yield
,后面没有“退出” 11 12
返回。
这种情况独立于内部for循环的奇偶校验(偶数/奇数)。唯一重要的是true
和yield
对及其处理方式。您必须了解,resume
不会在第一次调用yield
时在函数内返回任何值,而该值首先是在协程内部被调用的函数。