Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Confusion about lua corountine's resume and yield function

Tags:

coroutine

lua

I am learning lua by this video tutorial, it has this piece of code:

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))

The output is like this:

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

But I don't understand how yield and resume passes parameters to each other and why yield doesn't output the first 1,2 that resume passes to it, could someone please explain? Thanks

like image 488
Demonedge Avatar asked Dec 14 '22 05:12

Demonedge


1 Answers

Normal Lua functions have one entry (where arguments get passed in) and one exit (where return values are passed out):

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

The arguments are bound to the parameter names (local variables) by putting them inside the parentheses, the return values listed as part of the return statement can be retrieved by putting the function call expression in the right-hand side of an assignment statement, or inside of a larger expression (e.g. another function call).

There are alternative ways to call a function. E.g. pcall() calls a function and catches any runtime errors that may be raised inside. Arguments are passed in by putting them as arguments into the pcall() function call (right after the function itself). pcall() also prepends an additional return value that indicates whether the function exited normally or via an error. The inside of the called function is unchanged.

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

You can call the main function of a coroutine by using coroutine.resume() instead of pcall(). The way the arguments are passed, and the extra return value stays the same:

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

But with coroutines you get another way to (temporarily) exit the function: coroutine.yield(). You can pass values out via coroutine.yield() by putting them as arguments into the yield() function call. Those values can be retrieved outside as return values of the coroutine.resume() call instead of the normal return values.

However, you can re-enter the yielded coroutine by again calling coroutine.resume(). The coroutine continues where it left off, and the extra values passed to coroutine.resume() are available as return values of the yield() function call that suspended the coroutine before.

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

Note that the yield need not be directly in the main function of the coroutine, it can be in a nested function call. The execution jumps back to the coroutine.resume() that (re-)started the coroutine in the first place.

Now to your question why the 1, 2 from the first resume() doesn't appear in your output: Your coroutine main function doesn't list any parameters and so ignores all arguments that are passed to it (on first function entry). On a similar note, since your main function doesn't return any return values, the last resume() doesn't return any extra return values besides the true that indicates successful execution as well.

like image 190
siffiejoe Avatar answered Apr 17 '23 22:04

siffiejoe