【问题标题】:What is an elegant and concise way of iterating over multiple arrays in ruby?在 ruby​​ 中迭代多个数组的优雅而简洁的方法是什么?
【发布时间】:2013-07-06 13:19:41
【问题描述】:

我喜欢 Ruby 的地方在于它的优雅:如果我们将 injectmaptake_whileselect 一起使用,我们可以将块链接在一起以实现很多目标,同时减少编写代码。

坚持单行解决方案的想法,如何在 Ruby 中编写嵌套的 for 循环而不编写整个嵌套的 for 循环?我觉得这一定是可能的,我只是无法为我的生活弄清楚它是什么。我正在寻找这样的东西:

10.times {|a| 10.times {|b| a*b}}

我能想出的唯一优雅的解决方案是嵌套for 循环。谁有更好的解决方案?

array = []
for a in (1..10)
  for b in (1..10)
    array << a*b
  end
end

【问题讨论】:

  • “如何在 ruby​​ 中编写嵌套 for 循环而不编写整个嵌套 for 循环”?您必须以某种方式定义循环。你不能定义一个不完整的循环并让 Ruby 运行它,因为这会是一个语法错误。
  • 上述答案中的代码包含解析错误:解析器会将&lt;&lt;a*b 字符串视为HEREDOC 语法,而不是&lt;&lt;,然后是a*b。它们之间需要一个空格来分隔标记并避免解析错误。
  • @SasQ 好点,现在修复了。

标签: ruby loops


【解决方案1】:
arr = (1..10).map {|a| (1..10).map {|b| a*b}}.flatten

【讨论】:

  • 啊,这是我最初想做的,让我意识到我应该注意map and each之间的区别,但是谁能解释一下:(1..10).each {|a|a} =&gt; 1..10
  • #each 将返回迭代后传递给它的原始对象(在本例中为范围)。 #map 会将块的结果收集到一个数组中并返回。
【解决方案2】:
(1..10).to_a.product((1..10).to_a).map { |a,b| a*b }

http://ruby-doc.org/core-2.0/Array.html#method-i-product

【讨论】:

  • @BorisStitnicky 你认为这比上面的repeat_perutation 方法更有效吗?考虑到它将创建然后展平的中间数组,我认为它会更慢。
  • 基准测试表明#repeated_permutation 确实更快、更高效,这可能是您猜到的原因。它实际上是x.product(x)
【解决方案3】:

Array 有一些很酷的方法。

 Array(1..10).repeated_permutation(2).map {|a, b| a*b }

#repeated_permutation 将获取一个数组并生成一个包含该数组的所有排列的数组,该数组具有给定长度(在本例中为 2),允许重复(即,[1,1])。然后我们可以将每对的乘积映射到最终数组中。

您可以使用inject(:*) 来概括这一点。这将采用结果排列并将每个元素的所有元素相乘。例如,要生成(1*1*1*1)..(10*10*10*10)(导致输出集包含 10,000 个元素!):

Array(1..10).repeated_permutation(4).map {|v| v.inject :*}

【讨论】:

  • 这是一个有效的技巧,但不知何故,#repeated_permutation 不适合这个。 +1 用于教菜鸟该方法,但请不要这样使用它。
  • 您的 Matrix 解决方案代码更多,速度要慢一个数量级,并且使用几乎两倍的 RAM 来实现相同的结果。对于方阵,#repeated_permutation 就可以了。
  • 这正是我一直在寻找的东西,谢谢你教我一个很酷的新技巧!
【解决方案4】:
(1..10).inject([]) { |result,a| result + (1..10).to_a.map { |b| a*b } }

或者

def arithmetic(range, &block)
  range.inject([]) { |result,a| result + range.to_a.map { |b| block.call(a,b) } }
end

range = (1..10)
arithmetic(range) {|a,b| a*b }
arithmetic(range) {|a,b| a+b }

【讨论】:

    【解决方案5】:

    面对这样的问题,记住你的高中微积分:

    a = *1..10
    b = *1..10
    require 'matrix'
    
    Matrix.column_vector( a ) * Matrix[ b ]
    # or equivalent
    Matrix[ a ].transpose * Matrix[ b ]
    

    Matrix 是 Ruby 标准库的一部分,每个认真的 Ruby 演讲者都应该学习它的接口。

    【讨论】:

      【解决方案6】:

      查看这个问题的所有答案,我不觉得它们中的任何一个看起来比 OP 的嵌套 for 循环更“优雅”或更容易阅读。如果您想要一个不那么冗长的嵌套迭代符号,我认为您不会比定义自己的速记更好。比如:

       module Enumerable
         def combinations(*others)
           return enum_for(:combinations,*others) if not block_given?
           return if self.empty?
           if others.empty?
             self.each { |x| yield [x] }
           else
             others.first.combinations(*others.drop(1)) { |a| self.each { |x| yield (a + [x]) }}
           end
         end
       end
      

      定义了这个实用方法后,您可以将嵌套迭代示例编写为:

      array = []
      (1..10).combinations(1..10) { |a,b| array << a*b }
      

      【讨论】:

      • 我不确定我对enum_for 的调用是否正确,我必须检查一下。如果其他人知道正确的方法,请编辑。
      【解决方案7】:

      我能想出的唯一优雅的解决方案是嵌套 for 循环

      for-in 循环在 in 右侧的对象上调用 each(),因此 ruby​​ 专家不会使用 for-in 循环——他们直接在对象上调用 each()

      array = []
      
      (1..10).each do |a|
        (1..3).each do |b|
          array << a*b
        end
      end
      

      坚持单线解决方案的理念

      这样做几乎可以保证您不会编写优雅的 ruby​​ 代码——只需查看建议的解决方案即可。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-07-19
        • 1970-01-01
        • 1970-01-01
        • 2011-01-10
        • 2011-06-15
        • 2012-02-06
        相关资源
        最近更新 更多