Я начну с генераторов, видя, что это самый простой случай. Как уже упоминалось @zvolkov, это функции / объекты, которые можно вызывать многократно без возврата, но при вызове возвращает (выдает) значение и затем приостанавливает их выполнение. Когда они снова будут вызваны, они начнут с того места, где они в последний раз приостановили исполнение, и снова сделают свое дело.
Генератор - это, по сути, сокращенная (асимметричная) сопрограмма. Разница между сопрограммой и генератором заключается в том, что сопрограмма может принимать аргументы после того, как она была первоначально вызвана, тогда как генератор не может.
Немного сложно придумать тривиальный пример того, как вы будете использовать сопрограммы, но вот моя лучшая попытка. Возьмите этот (составленный) код Python в качестве примера.
def my_coroutine_body(*args):
while True:
# Do some funky stuff
*args = yield value_im_returning
# Do some more funky stuff
my_coro = make_coroutine(my_coroutine_body)
x = 0
while True:
# The coroutine does some funky stuff to x, and returns a new value.
x = my_coro(x)
print x
Примером использования сопрограмм являются лексеры и парсеры. Без сопрограмм в языке или каким-либо образом эмулированных, лексический и синтаксический анализ кода должны быть смешаны вместе, хотя на самом деле это две разные проблемы. Но используя сопрограмму, вы можете разделить код лексинга и синтаксического анализа.
(Я собираюсь зачеркнуть разницу между симметричными и асимметричными сопрограммами. Достаточно сказать, что они эквивалентны, вы можете конвертировать из одной в другую, и асимметричные сопрограммы - которые наиболее похожи на генераторы - -Просто понять. Я обрисовал в общих чертах, как можно реализовать асимметричные сопрограммы в Python.)
Продолжения на самом деле довольно простые звери. Все они являются функциями, представляющими другую точку в программе, которая, если вы вызовете ее, заставит выполнение автоматически переключиться на точку, которую представляет функция. Вы используете очень ограниченные версии их каждый день, даже не осознавая этого. Например, исключения можно рассматривать как своеобразное продолжение наизнанку. Я дам вам пример продолжения на основе Python для псевдокода.
Скажем, в Python была функция с именем callcc()
, и эта функция принимала два аргумента, первый из которых был функцией, а второй - список аргументов для вызова. Единственным ограничением для этой функции будет то, что последним аргументом, который она примет, будет функция (которая будет нашим текущим продолжением).
def foo(x, y, cc):
cc(max(x, y))
biggest = callcc(foo, [23, 42])
print biggest
Что произойдет, это то, что callcc()
в свою очередь вызовет foo()
с текущим продолжением (cc
), то есть ссылкой на точку в программе, в которой был вызван callcc()
. Когда foo()
вызывает текущее продолжение, это по сути то же самое, что сказать callcc()
вернуть значение, с которым вы вызываете текущее продолжение, и когда это происходит, он откатывает стек туда, где было создано текущее продолжение т.е. когда вы звонили callcc()
.
Результатом всего этого будет то, что наш гипотетический вариант Python выведет '42'
.
Надеюсь, это поможет, и я уверен, что моё объяснение можно немного улучшить!