各种语言中的Python生成器
你是怎么在自己喜欢的编程语言中模拟Python风格的生成器的呢?我在Scheme语言中发现了这个实现。看到其他语言的实现一定很有意思,特别是那些没有“第一类继续”的语言。
9 个回答
在JavaScript 1.7及以上版本中,我通常只需要加几个括号和大括号。其他的内容基本上没什么变化。JavaScript 1.7引入了类似Python的生成器和迭代器等新特性。
生成器表达式:
# Python
(x + 1 for x in y if x > 100)
// JavaScript 1.8+
(x + 1 for (x in y) if (x > 100))
生成器
# Python
def simpleRange(n):
for i in xrange(n):
yield i
for n in simpleRange(5):
print(n)
// JavaScript 1.7+
function simpleRange(n) {
for (let i = 0; i < n; i++)
yield i;
}
for (n in simpleRange(5))
print(n);
列表/数组推导式
# Python
[x + 1 for x in y if x > 100]
// JavaScript 1.7+
[x + 1 for (x in y) if (x > 100)]
我在Lisp/Scheme中根本不会使用yield。
'yield'需要语言中有某种协同程序或继续执行的功能。很多使用yield的地方其实可以用更简单的函数方式来实现。
YIELD基本上和著名的COME-FROM操作有关。 ;-) 在这里,某个地方的调用可以根据执行的上下文,导致在其他例程中的不同地方。所以一个例程突然有了多个入口点,这些入口点的顺序是在运行时决定的。对于简单的用法,这可能没问题,但我认为对于更复杂的代码,理解起来会变得更加困难。
拿问题中链接的Scheme例子来说:
(define/y (step)
(yield 1)
(yield 2)
(yield 3)
'finished)
(list (step) (step) (step))
多次调用(step)会返回不同的值。
我会创建一个闭包:
(define step
(let ((state '(1 2 3 finished)))
(lambda ()
(pop state))))
这将上面的带有yield的函数分成两部分:一个变量用来保存状态,一个简单的函数用来改变状态。状态不再隐含在执行顺序中。
(list (step) (step) (step))))
可以想象类似的解决方案来处理其他使用yield的情况。
把这个和Common Lisp SERIES库中的生成器进行比较:
(let ((x (generator (scan '(1 2 3 finished)))))
(list (next-in x)
(next-in x)
(next-in x)))
如果我们看看另一个答案中的这个Python例子
def simpleRange(n):
for i in xrange(n):
yield i
for n in simpleRange(5):
print(n)
我们可以看到它重复了控制结构。调用的地方和生成器都使用了FOR循环控制结构。通过使用闭包,我们可以消除生成器内部控制结构的使用,只提供状态转换的代码。
这里有一个用C++写的例子,它通过纤程来模拟生成器:
“yield return”迭代器是一种语言特性,创建它的原因很简单:为了让事情变得更简单。通常来说,遍历整个集合会容易得多,只需要把所需的上下文信息存储在局部变量中,而不是去制作一个复杂的自定义迭代器对象,这种对象需要在后续的取值操作中保存它的状态。
还有一些基本的C语言函数 setjmp, longjmp 也可以实现类似的效果。
(Lua的协程就是用上述方法实现的)