10

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有类似的东西吗?

4

3 回答 3

12

Ruby 中常用的方法是适当地结合EnumerableArray方法来实现相同的目的:

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

这仅比列表推导长 4 个左右的字符,并且具有同样的表现力(当然,恕我直言,但由于列表推导只是列表 monad 的一种特殊应用,有人可能会争辩说,使用 Ruby 的收集方法可以充分重建它),同时不需要任何特殊语法。

于 2012-07-03T22:05:42.403 回答
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,因此使用典型的 ++ 方法通常productselect安全map

于 2012-07-03T21:49:08.340 回答
1

正如上面的 RBK 所建议的,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”或任何东西......)。

当然,这里 Python 的真正优势在于,如果您决定要懒惰地评估列表,您可以通过删除括号(或将它们转换为括号,具体取决于上下文)将您的理解转换为生成器表达式。但即使在那里,您也可以创建一个 Array#lazycomprehend 或其他东西。

于 2012-07-03T21:48:30.623 回答