Сопрограмма против продолжения против генератора

StackOverflow https://stackoverflow.com/questions/715758

Вопрос

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

Это было полезно?

Решение

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

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

Другие советы

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

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

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

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

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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top