Coroutine против продолжения против генератора - PullRequest
135 голосов
/ 04 апреля 2009

В чем разница между сопрограммой и продолжением и генератором?

Ответы [ 3 ]

116 голосов
/ 04 апреля 2009

Я начну с генераторов, видя, что это самый простой случай. Как уже упоминалось @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'.

Надеюсь, это поможет, и я уверен, что моё объяснение можно немного улучшить!

32 голосов
/ 04 апреля 2009

Сопрограмма - это одна из нескольких процедур, которые по очереди выполняют свою работу, а затем делают паузу, чтобы передать контроль другим сопрограммам в группе.

Продолжение - это «указатель на функцию», которую вы передаете какой-либо процедуре, которая должна быть выполнена («продолжено с»), когда эта процедура завершена.

Генератор (в .NET) - это языковая конструкция, которая может выплевывать значение, «приостанавливать» выполнение метода и затем переходить из той же точки, когда запрашивается следующее значение.

8 голосов
/ 31 августа 2011

В более новой версии Python вы можете отправлять значения в Генераторы с помощью generator.send(), что делает Генераторы Python эффективно сопрограммами.

Основное различие между генератором python и другим генератором, скажем, greenlet, заключается в том, что в python ваш yield value может только вернуться обратно к вызывающей стороне. Находясь в гринлете, target.switch(value) может привести вас к определенной целевой сопрограмме и дать значение, при котором target продолжит работать.

...