在Ruby和Python中使用yield创建列表

在Ruby和Python中使用yield创建列表,python,ruby,list,yield,Python,Ruby,List,Yield,我正在尝试用一种优雅的方法从一个函数中创建一个列表,该函数可以在Python和Ruby中生成值 在Python中: def foo(x): for i in range(x): if bar(i): yield i result = list(foo(100)) 在Ruby中: def foo(x) x.times {|i| yield i if bar(i)} end result = [] foo(100) {|x| result << x} 虽

我正在尝试用一种优雅的方法从一个函数中创建一个列表,该函数可以在Python和Ruby中生成值

在Python中:

def foo(x):
    for i in range(x):
        if bar(i): yield i 
result = list(foo(100))
在Ruby中:

def foo(x)
  x.times {|i| yield i if bar(i)}
end
result = []
foo(100) {|x| result << x}
虽然我喜欢使用这两种语言,但Ruby版本必须初始化列表,然后填充列表,这一直让我有点烦恼。Python的结果是简单的迭代,这非常好。Ruby的收益率调用了一个块,这也很棒,但是当我只想填充一个列表时,感觉有点笨拙

还有更优雅的红宝石风格吗


UPDATE对示例进行了重新修改,以显示从函数生成的值的数量不一定等于x。

我知道这并不完全是您想要的,但用ruby表达示例的更优雅的方式是:

result = Array.new(100) {|x| x*x}

任何涉及一个值范围的操作都最好使用一个范围,而不是时间和数组生成。

因此,对于您的新示例,请尝试以下方法:

def foo(x)
  (0..x).select { |i| bar(i) }
end

基本上,除非您自己编写迭代器,否则在Ruby中不需要经常使用yield。如果您不再尝试使用Ruby语法编写Python习语,您可能会做得更好。

对于Python版本,我将使用以下生成器表达式:

(i for i in range(x) if bar(i))
或者对于过滤值的这种特定情况,更简单一些

itertools.ifilter(bar,range(x))

对于stbuton发布的Python列表理解版本,如果需要生成器,请使用而不是range。range将在内存中创建整个列表。

yield表示ruby和python的不同含义。在ruby中,如果我没记错的话,您必须指定一个回调块,而python中的生成器可以传递给任何持有它们的人。

与使用ruby生成器的python代码完全相同的是:

def foo(x)
    Enumerator.new do |yielder|
        (0..x).each { |v| yielder.yield(v) if bar(v) }
    end
end

result = Array(foo(100))
在上面的示例中,列表是惰性生成的,就像Python示例中一样;见:

def bar(v); v % 2 == 0; end

f = foo(100)
f.next #=> 0
f.next #=> 2

Ruby和Python的收益在语义上是不同的。Python的收益率几乎比ruby的收益率更接近ruby的Fiber.yield。这个问题涉及到任何产生值的函数,不管是否涉及范围。你是对的,这不是我想要的。我之所以会从一个方法中让步,唯一的原因是我不知道到底有多少值会让步。在很多情况下,这两种语言对等价问题的处理方法完全不同。您甚至不需要to_a-Range includes Enumerable。只需1..x.select{| i | bar i}。我甚至不会称之为Python习语,请参见下面的Python建议。Python中的IMO生成器乱七八糟。它们“看起来”像函数,但一点也不像,在识别它们为生成器而不是函数之前,必须在函数中搜索“产量”。既然生成器“函数”无论如何都会返回生成器对象……为什么不让API像在Ruby中一样明确地构建生成器对象呢?在这里,Ruby似乎比Python更明确——Python试图将它们伪装成函数,这完全是错误的抽象。@banister:我对这种差异有不同的反应。就我对ruby的了解而言,仅仅接受一个函数引用参数,并用每个生成的值依次调用它,就可以实现ruby生成器的等价构造。这只需要第一类函数,Python也有第一类函数。python方法使用continuations,这提供了更深入的内容。关于yield的神奇语法对开发人员来说应该不太重要,因为它也可以从常规函数返回生成器对象,就像生成器理解一样。@recursive,我不太理解python中的等效构造是如何工作的,你能举个例子吗?对于record,Ruby生成器是使用fibers实现的,fibers是完全成熟的语言支持的co例程。此外,ruby具有一流的延续性:P@banister例如我可能错了。我最后一次看ruby是在1.9.1之前,即使在那时我也不能完全确定,但我对ruby生成器的理解是,接受函数引用或类似的东西需要它。每个生成的值将一次一个地传递给该引用函数。相比之下,在python中,生成器在默认情况下更像是惰性迭代器。我不知道ruby有连续体。很可能我对ruby不公平。@recursive,啊……是的,你说的是ruby的内部迭代器:array.each{I|put I}。它们接受一个匿名函数的块,并按您所说的那样工作。但是我说的是枚举数,它是外部迭代器;它们是使用光纤实现的,工作起来也像惰性迭代器。请看这里:这两种方法似乎是等效的,区别在于ruby通过使用Enumerator.new显式地创建生成器对象,而python隐式地使用类似于函数定义的方法。
def bar(v); v % 2 == 0; end

f = foo(100)
f.next #=> 0
f.next #=> 2