I need to use coroutnes but I don't understand them.
What I want: in my program on the screen a message has to move around while at the same time timers need to run and modem messages have to be recived in the background. I was quite glad when I found
this tutorial becouse right in the 2nd paragraph it mentions something that I will need, so I knew I was at the right place.
As HPWebcamAble says, you only think you need them
because you don't understand them. The code examples for
os.startTimer() demonstrate how to do that sort of thing without them.
On those few occasions where you might need to use coroutines, odds are the pre-built coroutine managers the
parallel API offers
will do the job for you.
But, since we're here…
After reading the tutorial a couple of times I was still baffled becouse to me it looked like coroutines (coroutine.resume) are simple function calls and coroutine.yield is the same as return.
coroutine.yield() and return are indeed very similar, but the main difference is that when a function returns, it can no longer be resumed.
The only difference that I noticed, after ensuring myself that the program in the tutorial indeed worked, was that if os.pullEvent() is called within a coroutine then the event won't be removed from the event queue so the other os.pullEvent() in the other coroutine can use it.
That depends on the coroutine manager you're using, but yes, most are built with that sort of mindset behind them.
In the tutorial it says when resuming coroutine(s) they start running in the background (kind of) in a pseudo-parallel way.
If you resume a coroutine, then the code which resumed it pauses until that coroutine yields or returns (about the same as if you call a function normally - the code doing the calling pauses until the function returns).
Once all(?) coroutines have yielded (unfortunately in the example program they return at the same time :l ) the ancestor gets to run again. If I'm right about this, my question is: How a 2nd,3rd, nth coroutine is started if the ancestor is halted right after the 1st coroutine is called? Or the halt doesn't go for coroutine-related commands?
Rather, the "ancestor" code gets control back as soon as
any coroutine it resumes yields/returns. Because it also pauses as soon as it resumes
any coroutine, what's happening is that it'll resume the first, wait to get control back, then resume the second, wait to get control back, and so on - generally, you'd have your "ancestor" code (your coroutine manager) do this until all the coroutines have returned or errored (switching their status to "dead"). Or maybe you might only wait for some, or perhaps even none, of them to die. It's up to you.
Note that you wouldn't want to pass all events to all coroutines - if one specifically asks for a "char" event (by calling os.pullEvent("char") for eg), then you'd only resume that one when you got that type of event from the front of the event queue.
For example, parallel.waitForAny() runs all the functions you pass it as coroutines, and keeps going until any of them finish. When the first function it starts yields, it attempts to resume the second, and so on, simply looping through them. parallel.waitForAll() runs them until they
all finish. Again, these two pre-made coroutine managers (which also deal with the event filters for you) handle most use-cases.
Next question about the program is the evt = os.pullEvent() line. Why is it there? If my understanding is correct so far, the program would never even get there since after the coroutines yielded the if coroutine.status(getC) == "dead" would be true so the main function's while loop would be broken out of.
A coroutine that yields isn't "dead", it's "suspended". It's "dead" if it returns or errors.
os.pullEvent() calls os.pullEventRaw(), which calls coroutine.yield(). When a coroutine yields, the "ancestor" script carries on from where
it left off. When the coroutine is resumed, it carries on from where it yielded.
The last thing that I didn't understand at all is the unpack(evt). I understand what it does (if we don't know how much element an array has it returns the elements in proper format to be arguments). But why is it there? They are not used in the functions, or are they?
It's often convenient to store a bunch of arguments in a table, and unpack() provides a simple method with which to extract them.
Consider the source for os.pullEvent():
function os.pullEvent( sFilter )
local eventData = { os.pullEventRaw( sFilter ) }
if eventData[1] == "terminate" then
error( "Terminated", 0 )
end
return table.unpack( eventData )
end
It starts off by calling os.pullEventRaw(), which returns an event. What sort of event? Who knows! It could be a "char" event, in which case there'd be two parameters - "char" and whatever the character was. Or it could be a "mouse_click" event, in which case there'd be four parameters - "mouse_click", the button pressed, and the x/y co-ords of the press. They all get bundled into a table.
The function then proceeds to check if the event was a "terminate" event, and errors out of the script if so. Otherwise, it takes however many parameters the event contains, and returns them to the code that called os.pullEvent() in the first place - using unpack().
Hopefully this clarifies things somewhat, but again, the main thing to take away from all this is "you don't need coroutines for your stated objective".