关于 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
语句不匹配有关。它还与在匿名函数中调用 print
和 yield
的顺序有关。
让我们想象一下 print(coroutine.yield(i))
与迭代的执行图。
在第一次迭代中,我们 coroutine.resume
将 1
和 2
传递给协程。这是原点,所以我们不是从前yield
接取,而是匿名函数的原始调用本身。 yield
在打印中被调用,returning i=1
但 print
未被调用。函数退出。
接下来是功能暂停一段时间,然后我们会看到下一个 coroutine.resume
恢复。此 resume
通过 3
和 4
。函数 在最后 yield
开始。还记得 print
函数没有被调用,因为第一个 yield
函数首先被调用并退出了程序吗?好吧,在 print
中执行 returns,所以 print
现在被调用,但是这次 returning 3
和 4
作为这些是要传输的最新值。该函数再次重复,在 print
之前调用 yield
,returning i=2
。
如果我们继续迭代,我们将更明确地了解为什么我们没有看到 1
和 2
的潜在模式。我们的第一次迭代是 "exiting" yield 与对应的 "entering yield." 不成对 这对应于协程的第一次执行 co
.
我们可能希望最后一个 yield
也未配对,但不同之处在于我们将有一个 "entering" yield
未配对,而不是 "exiting" yield
未配对。这是因为循环已经结束了。这解释了为什么我们看到 11 12
后面跟着 true
后面没有 "exiting" yield
return.
这种情况与里面for循环的奇偶性(even/oddness)无关。唯一重要的是 resume
和 yield
对以及它们的处理方式。您必须明白 yield
不会 return 第一次调用 resume
时函数内的值,它首先用于调用协程内的函数。
我正在通过这个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
语句不匹配有关。它还与在匿名函数中调用 print
和 yield
的顺序有关。
让我们想象一下 print(coroutine.yield(i))
与迭代的执行图。
在第一次迭代中,我们 coroutine.resume
将 1
和 2
传递给协程。这是原点,所以我们不是从前yield
接取,而是匿名函数的原始调用本身。 yield
在打印中被调用,returning i=1
但 print
未被调用。函数退出。
接下来是功能暂停一段时间,然后我们会看到下一个 coroutine.resume
恢复。此 resume
通过 3
和 4
。函数 在最后 yield
开始。还记得 print
函数没有被调用,因为第一个 yield
函数首先被调用并退出了程序吗?好吧,在 print
中执行 returns,所以 print
现在被调用,但是这次 returning 3
和 4
作为这些是要传输的最新值。该函数再次重复,在 print
之前调用 yield
,returning i=2
。
如果我们继续迭代,我们将更明确地了解为什么我们没有看到 1
和 2
的潜在模式。我们的第一次迭代是 "exiting" yield 与对应的 "entering yield." 不成对 这对应于协程的第一次执行 co
.
我们可能希望最后一个 yield
也未配对,但不同之处在于我们将有一个 "entering" yield
未配对,而不是 "exiting" yield
未配对。这是因为循环已经结束了。这解释了为什么我们看到 11 12
后面跟着 true
后面没有 "exiting" yield
return.
这种情况与里面for循环的奇偶性(even/oddness)无关。唯一重要的是 resume
和 yield
对以及它们的处理方式。您必须明白 yield
不会 return 第一次调用 resume
时函数内的值,它首先用于调用协程内的函数。