В чем разница между сопрограммой и продолжением и генератором?
В чем разница между сопрограммой и продолжением и генератором?
Ответы:
Я начну с генераторов, посмотрев, как они являются простейшим случаем. Как уже упоминалось @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 вы можете отправлять значения в Generators с помощью generator.send()
, что делает Python Generators эффективно сопрограммами.
Основное отличие между Python Generator и другим генератором, скажем, greenlet, заключается в том, что в python вы yield value
можете только вернуться обратно к вызывающей стороне. Находясь в гринлете, target.switch(value)
вы можете перейти к определенной целевой сопрограмме и получить значение, при котором она target
будет продолжать работать.
yield
вызовы должны быть в одной и той же функции, которая называется «Генератор». Вы не можете сделать это yield
из подфункции, поэтому Python называют полу-сопрограммами , в то время как у Lua есть асимметричные сопрограммы . (Есть предложения по распространению урожая, но я думаю, что это только мутная вода.)