3

我试图想出一种优雅的方法来从一个在 Python 和 Ruby 中产生值的函数创建一个列表。

在 Python 中:

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

在红宝石中:

def foo(x)
  x.times {|i| yield i if bar(i)}
end
result = []
foo(100) {|x| result << x}

虽然我喜欢同时使用这两种语言,但我一直对 Ruby 版本必须初始化列表然后填充它感到有些困扰。Python 的yield结果是简单的迭代,这很棒。Rubyyield调用一个块,这也很棒,但是当我只想填写一个列表时,感觉有点笨拙。

有没有更优雅的 Ruby 方式?

更新重做示例以显示从函数产生的值的数量不一定等于 x。

4

7 回答 7

10

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

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

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

于 2009-03-04T02:34:45.233 回答
7

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

(i for i in range(x) if bar(i))

或者对于这种过滤值的特定情况,甚至更简单

itertools.ifilter(bar,range(x))
于 2009-03-04T02:53:40.007 回答
5

您的 Python 代码(使用 Ruby 生成器)的完全等价物是:

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
于 2010-09-23T17:58:46.580 回答
1

我知道这不是您要寻找的东西,但是用 ruby​​ 表达您的示例的更优雅的方式是:

result = Array.new(100) {|x| x*x}
于 2009-03-04T01:46:43.553 回答
1
def squares(x)
  (0..x).map { |i| i * i }
end

任何涉及值范围的事情都最好使用范围而不是times数组生成来处理。

于 2009-03-04T01:47:00.510 回答
1

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

于 2009-03-04T14:21:07.327 回答
1

yield意味着不同的东西红宝石和蟒蛇。在 ruby​​ 中,如果我没记错的话,你必须指定一个回调块,而 python 中的生成器可以传递给任何持有它们的人。

于 2009-03-04T14:34:17.890 回答