Ruby有类似Python的列表推导吗?

时间:2012-07-03 21:26:00

标签: python ruby programming-languages

Python有一个很好的功能:

print([j**2 for j in [2, 3, 4, 5]]) # => [4, 9, 16, 25]

在Ruby中,它甚至更简单:

puts [2, 3, 4, 5].map{|j| j**2}

但是如果它是关于嵌套循环的Python看起来更方便。

在Python中我们可以这样做:

digits = [1, 2, 3]
chars = ['a', 'b', 'c']    
print([str(d)+ch for d in digits for ch in chars if d >= 2 if ch == 'a'])    
# => ['2a', '3a']

Ruby中的等价物是:

digits = [1, 2, 3]
chars = ['a', 'b', 'c']
list = []
digits.each do |d|
    chars.each do |ch|
        list.push d.to_s << ch if d >= 2 && ch == 'a'
    end
end
puts list

Ruby有类似的东西吗?

3 个答案:

答案 0 :(得分:11)

Ruby中常见的方法是正确组合EnumerableArray方法来实现相同的目标:

digits.product(chars).select{ |d, ch| d >= 2 && ch == 'a' }.map(&:join)

这比列表理解只有4个左右的字符,而且就像表达一样(当然,恕我直言,但由于列表推导只是列表monad的一个特殊应用,人们可能会认为可能有可能充分重建使用Ruby的集合方法),但不需要任何特殊的语法。

答案 1 :(得分:2)

如你所知,Ruby没有列表推导的语法糖,所以你越接近就能以富有想象力的方式使用块。人们提出了不同的想法,看看lazylistverstehen方法,都支持嵌套的理解条件:

require 'lazylist'
list { [x, y] }.where(:x => [1, 2], :y => [3, 4]) { x+y>4 }.to_a
#=> [[1, 4], [2, 3], [2, 4]]

require 'verstehen'
list { [x, y] }.for(:x).in { [1, 2] }.for(:y).in { [3, 4] }.if { x+y>4 }.comprehend
#=> [[1, 4], [2, 3], [2, 4]]

当然,这不是您所谓的惯用Ruby,因此使用典型的product + select + map方法通常会更安全。

答案 2 :(得分:1)

正如上面的RBK所建议的那样,List comprehension in Ruby提供了大量不同的方法来处理Ruby中的列表推导。

它们都没有明确描述嵌套循环,但至少其中一些可以很容易地嵌套。

例如,Robert Gamble接受的答案建议添加一个Array#comprehend方法。

class Array
  def comprehend(&block)
    return self if block.nil?
    self.collect(&block).compact
  end
end

完成后,您可以将代码编写为:

digits.comprehend{|d| chars.comprehend{|ch| d.to_s+ch if ch =='a'} if d>=2}

与Python代码比较:

[str(d)+ch for d in digits for ch in chars if d >= 2 if ch == 'a']

差异很小:

  1. Ruby代码有点长。但这主要是说明了“理解”的事实;如果你愿意,你可以随时称它为更短的东西。
  2. Ruby代码以不同的顺序放置 - 数组从一开始而不是在中间。但是如果你考虑一下,那就是你所期望的,并且想要的,因为“一切都是一种方法”的哲学。
  3. Ruby代码需要嵌套的括号用于嵌套的理解。我不能想到一个明显的方法,这不会让事情变得更糟(你不想打电话给“[str,digits] .comprehend2”或任何东西......)。
  4. 当然,Python的真正优势在于,如果您决定要懒惰地评估列表,可以通过删除括号(或根据上下文将它们转换为括号)将您的理解转换为生成器表达式。但即使在那里,你也可以创建一个Array#lazycomprehend等等。