Generatoren
Jedes Mal coroutine.resume zu schreiben und das true aufzufangen, wird mühsam. Wenn du nur die Werte willst, die eine Koroutine mit yield abgibt, benutze coroutine.wrap. Es macht ebenfalls eine Koroutine, gibt dir aber eine Funktion zurück: Jeder Aufruf dieser Funktion setzt die Koroutine fort und gibt nur die Werte aus yield zurück, ohne das true:
-- Emmas Saatbeutel
local naechsteSaat = coroutine.wrap(function()
coroutine.yield("Karotte")
coroutine.yield("Kartoffel")
coroutine.yield("Kürbis")
end)
print(naechsteSaat())
print(naechsteSaat())
print("Letzte Saat: " .. naechsteSaat())Das gibt Folgendes aus:
Karotte
Kartoffel
Letzte Saat: KürbisDie Funktion für die Koroutine ist hier anonym und steht direkt in den Klammern, wie du es aus der Lektion über Funktionen kennst. Eine Funktion, die bei jedem Aufruf den nächsten Wert liefert, heißt Generator.
Generatoren in for. Einen hast du schon benutzt: for wort in text:gmatch("%a+") do aus der Lektion über Zeichenketten. gmatch gibt eine Funktion zurück, und for … in ruft sie vor jedem Durchlauf der Schleife auf. Was sie zurückgibt, geht in die Schleifenvariablen; gibt sie nil oder nichts zurück, endet die Schleife. Ein Generator aus coroutine.wrap passt genau hinein: Erreicht seine Funktion ihr Ende, gibt der Aufruf nichts zurück, und die Schleife hört auf.
Meistens schreibst du eine Funktion, die ein paar Einstellungen bekommt und einen neuen Generator zurückgibt. Dieser geht die Beete eines Feldes durch, Reihe für Reihe:
-- Jedes Beet des Feldes
local function beete(reihen, spalten)
return coroutine.wrap(function()
for reihe = 1, reihen do
for spalte = 1, spalten do
coroutine.yield(reihe, spalte)
end
end
end)
end
for reihe, spalte in beete(2, 2) do
print("Beet " .. reihe .. "-" .. spalte)
endDas gibt Folgendes aus:
Beet 1-1
Beet 1-2
Beet 2-1
Beet 2-2Die verschachtelten Schleifen sind im Generator versteckt, und die Schleife, die ihn benutzt, ist so einfach wie eine Schleife mit ipairs. Zwischen den Aufrufen merkt sich die Koroutine, wo sie in beiden Schleifen ist. Jeder Aufruf von beete(2, 2) erzeugt einen neuen Generator, der wieder bei Beet 1-1 beginnt.
Achtung:
- Ein Generator ist verbraucht, sobald seine Funktion zu Ende ist. Rufst du ihn noch einmal auf, stoppt das Programm mit
cannot resume dead coroutine. Für eine weitere Runde erzeuge einen neuen Generator. - Eine gewöhnliche Funktion mit
yielddarin, die nicht mitcoroutine.wrapgemacht wurde, funktioniert infornicht: Die Schleife führt sie als gewöhnliche Funktion aus, und ihryieldscheitert mitattempt to yield from outside a coroutine. - Eine Koroutine aus
coroutine.createist keine Funktion, also funktioniert sie inforauch nicht:attempt to call a thread value. Fürforbenutzewrap.
So schreibst du es:
Anweisungen
- 1
Die Funktion
findeReifegibt die Pflanze jedes Beets ab, das lange genug gewachsen ist. Mache daraus einen Generator,local naechsteReife = coroutine.wrap(findeReife), rufe ihn zweimal auf und gib aus, was er zurückgibt. Das Programm gibt aus:Karotte Weizen - 2
Ernte jetzt das ganze Feld. Gehe mit einer neuen Schleife über einen neuen Generator,
for pflanze in coroutine.wrap(findeReife) do, und gib für jedes reife BeetErnte:und seine Pflanze aus. (Warum ein neuer?naechsteReifehat schon zwei Pflanzen abgegeben und würde mit der dritten weitermachen.) Das Programm gibt aus:Karotte Weizen Ernte: Karotte Ernte: Weizen Ernte: Erdbeere