Итоги
На этом уроке вы узнали, что:
- Сопрограмма — это функция, которая умеет приостанавливаться посередине и потом продолжать с того же места, сохраняя свои локальные переменные. Игры используют сопрограммы для дел, которые занимают несколько ходов или кадров.
coroutine.create(f)создаёт сопрограмму из функцииf(послеfнет скобок).coroutine.resume(co)запускает её до следующегоcoroutine.yield()или до конца функции. Сопрограмма — значение типаthread, а не функция, поэтомуco()падает с ошибкойattempt to call a thread value.coroutine.status(co)— этоsuspended(ещё не запускалась или на паузе),runningилиdead(завершилась).coroutine.yield()вне сопрограммы, например в функции, которую вы вызвали сами, заканчивается ошибкойattempt to yield from outside a coroutine.coroutine.yield(v)отдаётvнаружу:resumeвозвращаетtrueиv.coroutine.resume(co, x)передаётxвнутрь: первыйresume— в параметры функции, каждый следующий — как результат тогоyield, на котором сопрограмма ждёт.- Цикл
while true doсyieldвнутри не бесконечный: он останавливается на каждомyield. coroutine.wrap(f)возвращает генератор: функцию, которая возобновляет сопрограмму и возвращает только значения изyield. Он подходит дляfor … in, который останавливается, когда функция сопрограммы заканчивается. Исчерпанный генератор нельзя запустить заново; создайте новый.- В игре у каждого персонажа может быть сопрограмма с его сценарием. Игровой цикл возобновляет каждую из них один раз за ход. Они ходят по очереди и никогда не работают одновременно.
- Ошибка внутри сопрограммы останавливает только её саму:
resumeвозвращаетfalseи сообщение, а сопрограмма становитсяdead. Возобновление мёртвой сопрограммы даётfalseиcannot resume dead coroutine. Поэтому проверяйте первое значение. Генератор изwrapпередаёт ошибки дальше, и программа останавливается.
В следующем уроке, итоговом проекте, вы соберёте весь курс воедино: сезон на ферме Эммы, одна программа, написанная шаг за шагом, от покупки семян до продажи урожая. Торговец Гуго там тоже будет, конечно, в виде сопрограммы.
Но сначала потренируйтесь на материале этого урока. Эмма собирается поливать поле; список её грядок уже в редакторе.
Задание
- 1
Напишите функцию
thirstyPlots(plots), которая возвращает генератор, созданный черезcoroutine.wrap. Генератор отдаёт каждую грядку из спискаplots, у которойwaterбольше0. Пройдите циклом поthirstyPlots(field)и для каждой грядки выведите культуру и количество воды так:Полить: морковь (2 л) Полить: тыквы (3 л) Полить: картофель (2 л) Полить: помидоры (1 л) - 2
Лейка Эммы — это сопрограмма. Напишите функцию
wateringCan(liters)с бесконечным циклом. В цикле лейка отдаёт (через yield), сколько литров в ней осталось, и ждёт, сколько нужно вылить,local amount = coroutine.yield(liters), а затем выливает: вычитаетamountизliters. Создайте из неё сопрограммуcan, запустите её черезcoroutine.resume(can, 6)и выведите, сколько в ней воды. Затем пройдите циклом по новому генераторуthirstyPlots(field), возобновляйтеcanсо значениемwaterкаждой грядки и выводите остаток. Под списком из первого задания программа выводит (последние две строки неправильные, вы исправите их в следующем задании):В лейке 6 л морковь: осталось 4 л тыквы: осталось 1 л картофель: осталось -1 л помидоры: осталось -2 л - 3
Лейка не может вылить больше воды, чем в ней есть. В
wateringCan, когдаamountбольшеliters, завершайте работу черезerror("не хватает воды", 0). В цикле по грядкам проверяйте, что возвращаетresume: если произошла ошибка, выведите культуру и сообщение и прервите цикл черезbreak. После цикла выведитеЭмма идёт к колодцу.Вся программа выводит:Полить: морковь (2 л) Полить: тыквы (3 л) Полить: картофель (2 л) Полить: помидоры (1 л) В лейке 6 л морковь: осталось 4 л тыквы: осталось 1 л картофель: не хватает воды Эмма идёт к колодцу.