Передача значений
Сопрограмма и программа, которая её возобновляет, могут передавать друг другу значения. В обе стороны всё идёт через одни и те же два места: yield и resume.
Из сопрограммы. То, что вы поставите в скобках coroutine.yield(…), выйдет из coroutine.resume. Куры Эммы каждый день несут разное количество яиц:
-- Яйца за три дня
local function henHouse()
coroutine.yield(4)
coroutine.yield(6)
coroutine.yield(5)
end
local hens = coroutine.create(henHouse)
print(coroutine.resume(hens))
print(coroutine.resume(hens))
local ok, eggs = coroutine.resume(hens)
print("День 3, яиц: " .. eggs)Он выводит:
true 4
true 6
День 3, яиц: 5Сначала resume возвращает true, что означает «сопрограмма отработала без ошибки», а потом значения из yield. Так же ведёт себя pcall из урока о функциях (с false вы познакомитесь в конце этого урока). Чтобы работать с яйцами, поймайте оба значения: local ok, eggs = coroutine.resume(hens).
В сопрограмму. Значения передаются через resume, после самой сопрограммы. Первый resume передаёт их параметрам функции, как это делает обычный вызов. Каждый следующий resume передаёт их сопрограмме, которая ждёт на yield: они становятся результатом этого coroutine.yield():
-- Заказ на рынке
local function order(customer)
print(customer .. " хочет купить тыквы.")
local count = coroutine.yield()
print(customer .. " берёт тыквы, штук: " .. count .. ".")
end
local sale = coroutine.create(order)
coroutine.resume(sale, "Дедушка Джо")
print("Эмма пересчитывает тыквы.")
coroutine.resume(sale, 3)Он выводит:
Дедушка Джо хочет купить тыквы.
Эмма пересчитывает тыквы.
Дедушка Джо берёт тыквы, штук: 3.При первом resume строка "Дедушка Джо" попала в параметр customer. Число 3 вышло из coroutine.yield() в переменную count. А между этими моментами программа занималась другими делами.
В обе стороны сразу. Сопрограмма часто хранит значение и обновляет его при каждом resume. Эмма собирает клубнику, а корзина ведёт общий счёт:
-- Клубника в корзине
local function basket()
local total = 0
while true do
local picked = coroutine.yield(total)
total = total + picked
end
end
local strawberries = coroutine.create(basket)
coroutine.resume(strawberries)
local ok, total = coroutine.resume(strawberries, 12)
print("В корзине: " .. total)
ok, total = coroutine.resume(strawberries, 8)
print("В корзине: " .. total)Он выводит:
В корзине: 12
В корзине: 20Шаг за шагом: первый resume запускает функцию, total равно 0, и сопрограмма останавливается на yield(total). Второй resume передаёт 12: yield возвращает это число в picked, сумма становится равной 12, цикл доходит до yield(total), и тот отдаёт 12 как результат resume. Третий resume делает то же самое с 8.
Обратите внимание на две вещи. Цикл while true do здесь не бесконечный: он останавливается на каждом yield и идёт дальше, только когда кто-то возобновит сопрограмму. Когда программа заканчивается, сопрограмма просто остаётся на паузе, и это нормально. И ещё: первый resume только доводит сопрограмму до первого yield. Если бы он передал 12, число 12 попало бы в параметры basket, а их там нет, и оно бы пропало.
Задание
- 1
На конкурсе тыкв весы Эммы запоминают самую тяжёлую тыкву. В
scaleподprintдобавьте бесконечный цикл. В нём сопрограмма ждёт вес следующей тыквы:local weight = coroutine.yield(heaviest), и еслиweightбольшеheaviest, запоминает его. В конце программы взвесьте тыкву в 12 кг: возобновитеcontestсо значением12, поймайте результаты вlocal ok, heaviestи выведите самую тяжёлую тыкву так:Весы готовы. Самая тяжёлая: 12 кг - 2
Взвесьте ещё две тыквы, в 9 кг и в 15 кг, и после каждой выведите самую тяжёлую. Программа выводит:
Весы готовы. Самая тяжёлая: 12 кг Самая тяжёлая: 12 кг Самая тяжёлая: 15 кг