Generators
Writing coroutine.resume and catching the true every time gets tiring. When you only want the values a coroutine yields, use coroutine.wrap. It makes a coroutine too, but gives you a function instead: each call of that function resumes the coroutine and returns just the values from yield, without the true:
-- Emma's seed bag
local nextSeed = coroutine.wrap(function()
coroutine.yield("carrot")
coroutine.yield("potato")
coroutine.yield("pumpkin")
end)
print(nextSeed())
print(nextSeed())
print("Last one: " .. nextSeed())This prints:
carrot
potato
Last one: pumpkinThe function for the coroutine is anonymous here, written right inside the parentheses, as you know from the lesson on functions. A function that gives you the next value every time you call it is called a generator.
Generators in for. You've used one before: for word in text:gmatch("%a+") do from the lesson on strings. gmatch returns a function, and for … in calls it before every round of the loop. What it returns goes into the loop variables; when it returns nil or nothing, the loop ends. A generator from coroutine.wrap fits right in: when its function reaches its end, the call returns nothing, and the loop stops.
Usually, you write a function that takes some settings and returns a new generator. This one goes through the plots of a field, row by row:
-- Every plot of the field
local function plots(rows, columns)
return coroutine.wrap(function()
for row = 1, rows do
for column = 1, columns do
coroutine.yield(row, column)
end
end
end)
end
for row, column in plots(2, 2) do
print("Plot " .. row .. "-" .. column)
endThis prints:
Plot 1-1
Plot 1-2
Plot 2-1
Plot 2-2The nested loops are hidden in the generator, and the loop that uses it is as simple as a loop with ipairs. Between calls, the coroutine remembers where it is in both loops. Each call of plots(2, 2) makes a new generator, which starts again at plot 1-1.
Watch out:
- A generator is used up once its function has ended. Calling it again stops the program with
cannot resume dead coroutine. For another round, make a new generator. - An ordinary function with
yieldinside, not made bycoroutine.wrap, doesn't work infor: the loop runs it as an ordinary function, and itsyieldfails withattempt to yield from outside a coroutine. - A coroutine from
coroutine.createisn't a function, so it doesn't work inforeither:attempt to call a thread value. Forfor, usewrap.
Instructions
The function findReady yields the crop of every plot that has grown long enough. Make a generator of it, local nextReady = coroutine.wrap(findReady), call it twice and print what it returns. The program prints:
carrot
wheatNow harvest the whole field. Loop over a new generator, for crop in coroutine.wrap(findReady) do, and for each ready plot, print Harvest: and its crop. (Why a new one? nextReady has already given out two crops and would go on with the third.) The program prints:
carrot
wheat
Harvest: carrot
Harvest: wheat
Harvest: strawberryStart programming for free
3/6