【问题标题】:How to do a conditioned for loop in Ruby如何在 Ruby 中执行条件 for 循环
【发布时间】:2017-05-27 09:24:25
【问题描述】:

我对 Ruby 的语法感到惊讶,我只能用一个词来形容它:舒适。

编辑:我想我不清楚。我想要一种简单的方法来退出有条件的循环。

遗憾的是,我找不到如何在 Ruby 中执行此 Java 代码:

假设: 数组 = [1,2,3,4] 数组2 = [1,2,3,4]

boolean condition = false;
for(int i = 0; i < array.length && !condition; i++)
{
  for(int j = 0; j < array2.length && !condition; j++)
  {
     condition = (array[i] + array2[j] + 1 == 7);
  }
}

if(condition)
{
  System.out.println("Two elements of the arrays + 1 sum 7")
}

我喜欢 Ruby 的 one liner……但我什至无法在完全开环的情况下做到这一点……

我正在寻找这样的东西(each_while 是编造的):

array.each_while(condition &amp;&amp; condition2) { SomeAction }

在 Ruby 中最简单的方法是什么?

我使用的大多数循环都有退出条件来优化它们。我在网上找到的所有东西都不能被 Ruby 优美的语法所接受,因为它比 Java 还要糟糕,而且我们都知道 Java 并不漂亮。

我在网上找到的一些解决方案:

catch "BreakOuterLoop" do
  for i in 1..10
    print "out #{i}\n"
    for j in 1..10
      print "in #{j}\n"
      throw "BreakOuterLoop" if i+j > 16
    end
  end
end

太可怕了……

【问题讨论】:

  • 条件是什么?
  • array.length.times do 可以轻松做,但是你想在里面插入什么条件呢?
  • 任何条件。例如,布尔值现在为真,因为在“SomeAction”代码中它改变了它的值。
  • 这个问题恐怕有点太笼统了。请用一个具体的例子,一些数组和一些逻辑发布另一个问题。
  • 我确实改变了逻辑。请检查一下。它现在特定于一个问题。

标签: ruby loops for-loop


【解决方案1】:
array.length.times do |i|
  break unless condition_1 && condition_2
  # some action
end

break 将在条件不再满足时立即停止循环

关于循环矩阵

当然你可以在一个循环中嵌套一个循环,但这绝对不是 ruby 方式。我确信对于可能出现的任何问题都有更好的解决方案。

【讨论】:

    【解决方案2】:

    如前所述,对于您能想到的任何特定用例,都有一个更漂亮的功能解决方案。我将尝试回答更普遍的问题,即如何转换此 java 代码:

    int somethingYouWillMutate = 0; // any types or values or number of things
    for(int i = 0; i < array.length && condition && condition2; i++) {
      for(int j = 0; j < array2.length && condition; j++) {
         someAction(array[i], array2[j], somethingYouWillMutate);
      }
    }
    

    到红宝石:

    something_you_will_mutate = 0
    array.product(array2).each do |e1, e2|
      break unless condition && condition2
    
      some_action(e1, e2)
    end
    

    注意

    while c1 && c2 =:=
    while true; break if !(c1 && c2) =:=
    while true; break unless c1 && c2
    

    如果你也想要索引:

    array_indexed  = array.each_with_index.to_a
    array2_indexed = array2.each_with_index.to_a
    
    array_indexed.product(array2_indexed).each do |(e1, i1), (e2, i2)|
      break unless condition && condition2
    
      some_action(e1, e2, i1, i2, something_you_will_mutate)
    end
    

    注意:如果您想要更通用的解决方案(例如 3 个或更多数组):

    [array, array2, array3...].
      map(&:each_with_index).
      map(&:to_a).
      reduce(:product).
      map(&:flatten).
      each do |e1, i1, e2, i2, e3, i3...|
        break unless condition && condition2 && condition3...
    
        some_action(e1, e2, i1, i2, e3, i3..., something_you_will_mutate)
      end
    

    【讨论】:

    • 这不起作用,因为条件将在“每个”迭代之前进行评估。如果条件取决于“每个”迭代中的操作怎么办?
    • @ndn:我不这么认为,因为_whatever_will_be_needed_in_both 可能依赖于_co‌​ntinue_the_action_。您还需要在某处使用lazy,以避免在不需要的元素上使用map
    • @EricDuminil,第一点 - 它不能,这就是他们被这样称呼的原因。对于第二点 - 确实需要惰性。
    • @ndn 嘿,对不起。 take_while 确实与懒惰的人一起工作。我太新了,不知道懒惰的事情。
    • @AFP_555,用更通用的东西替换了我的答案。
    【解决方案3】:

    条件循环

    你可以使用break

    array1.each do |x|
      break unless condition && condition2
        array2.each do |y|
          break unless condition3
          # do_something
        end
      end
    end
    

    如果您需要条件中的索引:

    array1.each_with_index do |x,i|
      break unless condition && condition2
        array2.each_with_index do |y,j|
          break unless condition3
          # do_something
        end
      end
    end
    

    具体问题

    布尔值

    对于您更新的问题,您可以使用any?。这正是你想做的。只要条件不是true,它就会迭代,并尽快返回一个值:

    array  = [1,2,3,4]
    array2 = [1,2,3,4]
    
    puts array.product(array2).any?{|a,b| a + b + 1 == 7 }
    #=> true
    

    或者:

    puts array.any?{|a| array2.any?{ |b| a + b + 1 == 7 } }
    #=> true
    
    puts array.any?{|a| array2.any?{ |b| a + b + 1 == 12 } }
    #=> false
    

    第二个例子应该更快,因为不是每对都被创建:一旦找到,true 就会被返回。

    如果你想知道条件是true,你可以使用find

    p array.product(array2).find { |a, b| a + b + 1 == 7 }
    #=> [2,4]
    
    p array.product(array2).find { |a, b| a + b + 1 == 12 }
    #=> nil
    

    大数组优化

    对于大型数组,上述代码运行缓慢。

    您可以将最大的数组转换为 Set,并使用直接查找:

    require 'set'
    
    array  = [1, 2, 3, 4]
    array2 = [1, 2, 3, 4]
    set2 = array2.to_set
    
    sum = 7 - 1
    x1 = array.find { |x| set2.include?(sum - x) }
    
    if x1
      puts "#{x1} + #{sum - x1} + 1 = #{sum + 1}"
    end
    
    #=> 2 + 4 + 1 = 7
    

    【讨论】:

    • 在庞大的数组和错误的条件下浪费数十亿次虚拟迭代 :)
    • 是啊,但它太丑了...如果我有三个嵌套循环并且每个循环都有条件怎么办?
    • @mudasobwa 更新了它。感谢您的评论。
    • 好的,我正在检查这个答案。我认为这是最接近体面代码的方法。对 Ruby 有点失望……条件迭代并不罕见。
    • 现在对 Ruby 感到失望还为时过早。就像你可以用任何语言编写 FORTRAN 一样,如果你用 Ruby 编写 Java 代码,感觉有点不对劲。请发布一个包含数据和逻辑的具体问题,您会被 Enumerables 的功能所震撼。
    【解决方案4】:
    require 'matrix'
    
    Matrix[0...rows, 0...cols].each_with_index do |e, row, col|
      next unless [cond1, cond2, cond3].reduce :&
      # code
    end
    

    array1.each.with_index.lazy.take_while { cond1 }.each do |e1, i1|
      array2.each.with_index.lazy.take_while { cond2 }.each do |e2, i2|
        # do some stuff
      end
    end
    

    【讨论】:

    • 一位智者曾经告诉我,我“在庞大的数组和错误的条件下浪费了数十亿次虚拟迭代”;)
    • @EricDuminil bah :) 这是在假设条件可能会来回变化的情况下编写的,在这种情况下,我们必须遍历所有内容。
    • 好的。这(第二种方法)是我一直在寻找的。这是解决条件 for 循环的一般方法。
    • @mudasobwa:您可以在块内添加p :condition1p [e1,i1] 以表明它们是交错的。
    猜你喜欢
    • 2020-05-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-03-23
    • 1970-01-01
    • 2018-09-26
    • 1970-01-01
    • 2017-12-27
    相关资源
    最近更新 更多