Coroutines? Advanced Tutorial

Coroutines in Lua are a powerful tool that can be used to create sophisticated programs. In this tutorial, we will explore some of the more advanced features of coroutines.

First, let’s review the basics. A coroutine in Lua is a function that can be suspended and resumed. When a coroutine is suspended, it can be resumed later, allowing it to pick up where it left off.

Coroutines can be created using the coroutine.create() function:

local co = coroutine.create(function()
    print("Hello, world!")
end)

To start a coroutine, we use the coroutine.resume() function:

coroutine.resume(co)

This will print “Hello, world!” to the console.

Now that we know the basics, let’s look at some more advanced features.

One advanced feature of coroutines is the ability to yield values. To do this, we use the coroutine.yield() function:

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

coroutine.resume(co)

This code will print the numbers 1 through 10, one at a time. Each time the coroutine yields, it suspends itself and can be resumed later.

Another advanced feature is the ability to pass values into a coroutine when it is resumed. To do this, we simply pass values as arguments to the coroutine.resume() function:

local co = coroutine.create(function(a, b)
    print(a, b)
end)

coroutine.resume(co, 1, 2)

This will print “1 2” to the console.

Coroutines can also be used to create infinite loops. To do this, we use the coroutine.wrap() function:

local co = coroutine.wrap(function()
    while true do
        print("Hello, world!")
        coroutine.yield()
    end
end)

co()

This code will print “Hello, world!” to the console indefinitely.

Finally, let’s look at how we can use coroutines to create complex programs.

Suppose we have a program that needs to do two things: print “Hello, world!” to the console, and then wait for the user to press the Enter key. We can use coroutines to do this:

local printHello = coroutine.create(function()
    print("Hello, world!")
    coroutine.yield()
end)

local waitForEnter = coroutine.create(function()
    local line = io.read()
    coroutine.yield(line)
end)

coroutine.resume(printHello)
local line = coroutine.resume(waitForEnter)

print(line)

This code will print “Hello, world!” and then wait for the user to press the Enter key. When the Enter key is pressed, the line of text that the user entered will be printed to the console.

Coroutines are a powerful tool that can be used to create sophisticated programs. In this tutorial, we have explored some of the more advanced features of coroutines.

Coroutines

Coroutines in Lua are like functions, but they can be stopped before they’re done, and can return in multiple places, with multiple values. This can be useful in some cases.

To use them, we can create a Lua file with a few functions in it. We’ll use the five-function approach, so we can stick everything in one file.

function new(str, time)
	return coroutine.create(function()
		local difficulty = "easy"
		while not difficulty:match("easy|hard|main") do
			difficulty = tostring(text.get({"stuff"}))
		end
		local t = assert(loadstring(str))(difficulty)
		local d_time = clock.getTime()
		for i = 1, t do
			event.pull(time, "interrupted")
			main_loop()
		end
		local e_time = clock.getTime()
		return e_time - d_time - #t * time
	end)
end

function resume(coro, ...)
	local result = {coroutine.resume(coro, ...)}
	require "table".remove(result, 1)
	if coroutine.status(coro) == "dead" then
		return unpack(result)
	end
	return true, unpack(result)
end
function yield(...)
	coroutine.yield(...)
end
function status(coro)
	return coroutine.status(coro)
end
function running()
	return coroutine.running()
end
return require "lib" and "Update component" or require "component".update

Don’t worry about what’s going on, we’ll focus on the parts you can use. Just remember that this file is the coroutine file, and save it on your machine. We’ll be working with three different coroutines in our file, each having different timeouts and behaviours, Let’s call it coroutine.lua to make things simple.

#Creating a new Coroutine

To create a new coroutine, we want to call the new function from a different lua file. Let’s create another lua file in the same folder and write some things in it.
```
local component = require “component” --Ethernet card (or Modem card)
local cr = require(“coroutine”)

local c = cr.new("return 5", 0.5) --Every half a second, even if it was interrupted by an event.

local f = cr.new("return 5", 0.1) --Every tenth of a second
local f2 = cr.new("return 5", 0) --Everytime

while true do
	event.pull("key_up") --Use any type of event, or make your own event.
	-->if event.pull("key_up") and
	print("I'm done waiting! Into the loop I go!")
	if cr.resume(f) then --Resume and get it's value.
		print("I timed out!") --Never prints
	else
		print("I got a value!") --Everytime
	end
	-->end
	print("I'm continuing the loop... why is there something around me?")
end
```
So, what's going on here? Let's take a step back and remember how Lua files work.

#Main Loop

When you make a script, you can run ```while true do``` to make a "Main Loop". Anything that'll be called by the loop will be called over and over, and this is how a program works. _Note that you can also use the kernel event on oc_. But, either way works! Back to the script...

	#The Script
	First, we make a coroutine by giving the file with coroutines the call to initialize. It gives us an object, which we can use to manipulate the coroutine. From there, we're going to use a while loop to make a main loop with the event ```key_up```. Try typing someting other than ```while true do``'. Instead, try ```while requires.get("f") do``` and see what happens! Either way, we'll return to the loop once the event is called, since the main loop runs over and over... _and over again_. But then, _after_ the event was called, we want to return a value from the coroutine, and if it timed out, we'll be sure of it! Validation and everything!

		Ok, so there's a few things to understand here. The _true_ indicates if it succeeded in returning a value, and the value is what it returns from the loop, which we defined as our string to a table of 5. So we'll always get a timed out after one timeout and a value of 5.

		Finally, we become ```f2```, which was initialized with a timeout of 0. The object will never time out, so the coroutine will return a value of 5 every time we run the loop and call the function ```resume```.

			#Calling resume

			There are two ways to call resume. Either you can send any time you want, like with f in our excercise, or you can send no time. Using no time, like with f2, will call the resume with a value of 0, which is equal to no timeout. In the end, no timeout means the coroutine will resume everytime the script runs, but it'll return from the coroutine after all the loops run in the file to coroutine. So you'd get a loop of _a kind_, with two main loops! We'll keep our look simple, though.

	#Getting values

	So far, all we've done is check whether we received a value or not. But what if we wanted to assign the value to a variable? We'd have to get it somehow. If you remember your algebra, you can assign the returned values to three variables. So, you'd do something like ```true, 5 = cr.resume(coroutine)```, and lua would understand that you want the first value in the list to true, so it also knows to assign the rest of the values to ```5```.

	And that's how you work with coroutines. They might seem like magic, but there's nothing special about them.

Entension

Coroutine is a powerful tool that can be used to write complex procedures in Lua. In this tutorial, we will explore how coroutine can be used to write complex procedures. We will also provide coded examples to illustrate how coroutine can be used.

Coroutine is a powerful tool that can be used to write complex procedures in Lua. In this tutorial, we will explore how coroutine can be used to write complex procedures. We will also provide coded examples to illustrate how coroutine can be used.

Coroutine can be used to write complex procedures in Lua by breaking the procedure down into smaller tasks that can be executed one at a time. This allows the programmer to write more complex procedures without having to worry about the order in which the tasks are executed.

To use coroutine, we first need to create a coroutine object. We can do this by calling the coroutine.create() function. This function takes a function as an argument. This function will be the task that the coroutine object will execute.

Next, we need to start the coroutine by calling the coroutine.resume() function. This function will start the execution of the function that we passed to the coroutine.create() function.

We can also pass arguments to the coroutine.resume() function. These arguments will be passed to the function that we passed to the coroutine.create() function.

Once the coroutine has started executing, we can call the coroutine.yield() function to suspend the execution of the coroutine. This function takes an optional argument. This argument will be returned by the coroutine.resume() function when the coroutine is resumed.

We can resume the execution of the coroutine by calling the coroutine.resume() function again. This function will resume the execution of the coroutine from the point at which it was last suspended.

It is important to note that the coroutine.yield() function will only suspend the execution of the coroutine if the coroutine is currently running. If the coroutine is not currently running, the coroutine.yield() function will return an error.

We can check if the coroutine is currently running by calling the coroutine.status() function. This function will return a string that indicates the status of the coroutine. The possible values that this function can return are “running”, “suspended”, “dead”, and “normal”.

If the coroutine is currently running, the coroutine.status() function will return “running”. If the coroutine is currently suspended, the coroutine.status() function will return “suspended”. If the coroutine has finished executing, the coroutine.status() function will return “dead”.

Finally, we can call the coroutine.destroy() function to destroy the coroutine. This function will terminate the execution of the coroutine and free all resources that were allocated to the coroutine.

It is important to note that the coroutine.destroy() function should only be called when the coroutine is no longer needed. Calling the coroutine.destroy() function while the coroutine is still running will result in an error.

Now that we have seen how coroutine can be used to write complex procedures in Lua, let’s take a look at a few examples.

Example 1

In this example, we will use coroutine to write a procedure that calculates the factorial of a number.

First, we will create a coroutine object by calling the coroutine.create() function. We will pass a function to this function that calculates the factorial of a number.

Next, we will start the coroutine by calling the coroutine.resume() function. We will pass the number 5 as an argument to this function.

Once the coroutine has started executing, we will call the coroutine.yield() function to suspend the execution of the coroutine.

Finally, we will resume the execution of the coroutine by calling the coroutine.resume() function again. This time, we will not pass any arguments to this function.

The code for this example is shown below.

local function factorial(n)
    if n == 0 then
        return 1
    else
        return n * factorial(n - 1)
    end
end
local co = coroutine.create(factorial)
coroutine.resume(co, 5)
coroutine.yield()
coroutine.resume(co)

Example 2

In this example, we will use coroutine to write a procedure that calculates the Fibonacci sequence.

First, we will create a coroutine object by calling the coroutine.create() function. We will pass a function to this function that calculates the Fibonacci sequence.

Next, we will start the coroutine by calling the coroutine.resume() function. We will pass the number 10 as an argument to this function.

Once the coroutine has started executing, we will call the coroutine.yield() function to suspend the execution of the coroutine.

Finally, we will resume the execution of the coroutine by calling the coroutine.resume() function again. This time, we will not pass any arguments to this function.

The code for this example is shown below.

local function fib(n)
    if n == 0 then
        return 0
    elseif n == 1 then
        return 1
    else
        return fib(n - 1) + fib(n - 2)
    end
end
local co = coroutine.create(fib)
coroutine.resume(co, 10)
coroutine.yield()
coroutine.resume(co)
12 Likes

A pretty underrated (imo) feature of coroutines would be their use as iterators, which has two cool features:

Functions in a coroutine iterator are only called when they’re needed. This can save performance if the loop is designed to exit out early before iterating through everything.

In this example, the iterator will iterate up to ten times, but it paused and skipped the remaining 4 loops.

Compare this to the more typical approach of generating the entire array first beforehand, and then iterating through them:

Coroutine iterators save performance by cutting out unnecessary iterations.


Coroutine iterators also allow you to return multiple outputs, more than the usual k, v pair you see so often.

This can also help with performance since you don’t need to create new tables to store multiple outputs, you can directly return all of them.

6 Likes

Coroutines in Lua can be used as iterators to improve performance. By using a coroutine to iterate over a table, we can avoid having to create a new table each time we need to iterate over it. This can be a significant performance gain, especially if the table is large.

To use a coroutine as an iterator, we first need to create a function that will return the next value in the sequence. This function must be a coroutine, and it must use the yield keyword to return the next value. For example, the following function will return the next Fibonacci number in the sequence:

function fibonacci () 
local a, b = 0, 1 
while true do 
coroutine.yield(a) a, b = b, a + b 
end end

This function can be used in a for loop like this:

for n in fibonacci() do 
print(n) 
if n > 100 then 
break
 end 
end

This will print the first 100 Fibonacci numbers.

To use a coroutine as an iterator, we need to create a function that will return the next value in the sequence. This function must be a coroutine, and it must use the yield keyword to return the next value. For example, the following function will return the next Fibonacci number in the sequence:

function fibonacci () 
local a, b = 0, 1 
while true do 
coroutine.yield(a) a, b = b, a + b 
end 
end

This function can be used in a for loop like this:

for n in fibonacci() do 
print(n) 
if n > 100 then 
break end 
end

This will print the first 100 Fibonacci numbers.

Coroutines can be used as iterators to improve performance because they avoid having to create a new table each time we need to iterate over it. This can be a significant performance gain, especially if the table is large.

2 Likes

I’m Learning coding too. Uhm…Thanks for the Tutorial!

Thanks! Fully Understandable!

1 Like