协程vs延续vs生成器 [英] Coroutine vs Continuation vs Generator

查看:82
本文介绍了协程vs延续vs生成器的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

协程和延续与生成器有什么区别?

解决方案

我将从生成器开始,看到因为它们是最简单的情况。如@zvolkov所述,它们是可以重复调用而无需返回的函数/对象,但是在调用时将返回(屈服)一个值,然后中止执行。再次调用它们时,它们将从上次暂停执行的位置开始执行,然后再次执行操作。



生成器本质上是简化(不对称)的协程。协程和生成器之间的区别在于,协程在最初被调用后就可以接受参数,而生成器则不能。



想起来有点困难一个使用协程的简单例子,但这是我的最佳尝试。以这个(组成的)Python代码为例。

  def my_coroutine_body(* args):
而True:
#做一些时髦的东西
* args = yield value_im_returning
#再做一些时髦的东西

my_coro = make_coroutine(my_coroutine_body)

x = 0
而True:
#协程对x做一些时髦的事情,并返回一个新值。
x = my_coro(x)
打印x

协程所在的示例使用的是词法分析器和解析器。没有语言的协程或以某种方式进行仿真,即使将词法分析和解析代码确实是两个独立的问题,也需要将它们混合在一起。但是使用协程,您可以分离出词法分析代码。



(我将讨论对称协程和非对称协程之间的区别。它们是等效的,您可以从一个转换为另一个,并且不对称协程(最类似于生成器)更容易理解。我正在概述一个人如何在Python中实现不对称协程。)



延续实际上是非常简单的野兽。它们全部是代表程序中另一点的函数,如果调用它,将导致执行自动切换到该函数代表的点。您每天都使用非常严格的版本,甚至没有意识到。例如,可以将异常视为一种由内而外的延续。我会给你一个基于Python的延续的伪代码示例。



说Python有一个叫做 callcc(),此函数带有两个参数,第一个是函数,第二个是调用它的参数列表。对该函数的唯一限制是它接受的最后一个参数将是一个函数(这将是我们当前的延续)。

  def foo(x,y,cc):
cc(max(x,y))

maximum = callcc(foo,[23,42] )
打印最大的

会发生什么是 callcc() 会依次调用 foo()和当前的延续( cc ),即,是对程序中调用 callcc()的点的引用。当 foo()调用当前的延续时,它实际上与告诉 callcc()返回带有您的值的相同正在调用当前延续,并且这样做时,它将回滚到创建当前延续的堆栈,即,当您调用 callcc()时。 / p>

所有这些操作的结果将是我们假设的Python变体将打印 '42'



我希望能对您有所帮助,并且我相信我的解释可以在很大程度上得到改善!


What is the difference between a coroutine and a continuation and a generator ?

解决方案

I'll start with generators, seeing as they're the simplest case. As @zvolkov mentioned, they're functions/objects that can be repeatedly called without returning, but when called will return (yield) a value and then suspend their execution. When they're called again, they will start up from where they last suspended execution and do their thing again.

A generator is essentially a cut down (asymmetric) coroutine. The difference between a coroutine and generator is that a coroutine can accept arguments after it's been initially called, whereas a generator can't.

It's a bit difficult to come up with a trivial example of where you'd use coroutines, but here's my best try. Take this (made up) Python code as an example.

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

An example of where coroutines are used is lexers and parsers. Without coroutines in the language or emulated somehow, lexing and parsing code needs to be mixed together even though they're really two separate concerns. But using a coroutine, you can separate out the lexing and parsing code.

(I'm going to brush over the difference between symmetric and asymmetric coroutines. Suffice it to say that they're equivalent, you can convert from one to the other, and asymmetric coroutines--which are the most like generators--are the easier to understand. I was outlining how one might implement asymmetric coroutines in Python.)

Continuations are actually quite simple beasts. All they are, are functions representing another point in the program which, if you call it, will cause execution to automatically switch to the point that function represents. You use very restricted versions of them every day without even realising it. Exceptions, for instance, can be thought of as a kind of inside-out continuation. I'll give you a Python based pseudocode example of a continuation.

Say Python had a function called callcc(), and this function took two arguments, the first being a function, and the second being a list of arguments to call it with. The only restriction on that function would be that the last argument it takes will be a function (which will be our current continuation).

def foo(x, y, cc):
   cc(max(x, y))

biggest = callcc(foo, [23, 42])
print biggest

What would happen is that callcc() would in turn call foo() with the current continuation (cc), that is, a reference to the point in the program at which callcc() was called. When foo() calls the current continuation, it's essentially the same as telling callcc() to return with the value you're calling the current continuation with, and when it does that, it rolls back the stack to where the current continuation was created, i.e., when you called callcc().

The result of all of this would be that our hypothetical Python variant would print '42'.

I hope that helps, and I'm sure my explanation can be improved on quite a bit!

这篇关于协程vs延续vs生成器的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆