【问题标题】:How to break outer cycle in Ruby?如何打破Ruby中的外循环?
【发布时间】:2010-11-24 00:57:00
【问题描述】:

在 Perl 中,可以像这样打破外部循环:

AAA: for my $stuff (@otherstuff) {
         for my $foo (@bar) {
             last AAA if (somethingbad());
         }
      }

(语法可能错误),它使用循环标签从内循环内部中断外循环。 Ruby中有没有类似的东西?

【问题讨论】:

    标签: ruby loops cycle


    【解决方案1】:

    您可以考虑添加一个设置在内循环内部的标志,用于控制外循环。

    'next' 外循环

    for i in (1 .. 5)
      next_outer_loop = false
      for j in (1 .. 5)
        if j > i     
          next_outer_loop = true if j % 2 == 0
          break      
        end          
        puts "i: #{i}, j: #{j}"
      end            
      print "i: #{i} "                                                                                                                                                                             
      if next_outer_loop
        puts "with 'next'"
        next         
      end            
      puts "withOUT 'next'"
    end
    

    '打破'外循环

    for i in (1 .. 5)
      break_outer_loop = false
      for j in (1 .. 5)
        if j > i
          break_outer_loop = true if i > 3
          break
        end
        puts "i: #{i}, j: #{j}"
      end
      break if break_outer_loop
      puts "i: #{i}"
    end
    

    【讨论】:

      【解决方案2】:

      在循环周围包裹一个内部方法可以解决问题 示例:

      test = [1,2,3]
      test.each do |num|
        def internalHelper
          for i in 0..3 
            for j in 0..3
              puts "this should happen only 3 times"
              if true
                return
              end
            end
          end
        end
      internalHelper
      end
      

      在这里,您可以在任何 for 循环中进行检查,并在满足条件后从内部方法返回。

      【讨论】:

        【解决方案3】:

        您想要的是非本地控制流,Ruby 有几种选择:

        • 继续,
        • 例外情况,以及
        • throw/catch

        继续

        优点:

        • 延续是非本地控制流的标准机制。事实上,您可以在它们之上构建任何非本地控制流(子例程、过程、函数、方法、协程、状态机、生成器、条件、异常):它们几乎是GOTO 的双胞胎更好。

        缺点:

        • Continuations 不是 Ruby 语言规范的强制部分,这意味着某些实现(XRuby、JRuby、Ruby.NET、IronRuby)没有实现它们。因此,您不能依赖它们。

        例外情况

        优点:

        • 有一篇论文从数学上证明异常可以比连续更强大。 IOW:它们可以做 continuation 可以做的所有事情,甚至更多,因此您可以将它们用作 continuation 的替代品。
        • 普遍存在例外情况。

        缺点:

        • 它们被称为“例外”,这使人们认为它们“仅用于特殊情况”。这意味着三件事:阅读您的代码的人可能不理解它,实现可能没有针对它进行优化(而且,是的,异常在几乎所有 Ruby 实现中都非常缓慢),最糟糕的是,您一旦他们看你的代码,就会不断地厌倦所有这些人,盲目地喋喋不休地喋喋不休地说“例外只适用于特殊情况”。 (当然,他们甚至不会试图理解你在做什么。)

        throw/catch

        这是(大致)它的样子:

        catch :aaa do
          stuff.each do |otherstuff|
            foo.each do |bar|
              throw :aaa if somethingbad
            end
          end
        end
        

        优点:

        • 同例外。
        • 在 Ruby 1.9 中,对控制流使用异常实际上是语言规范的一部分!循环、枚举器、迭代器等都使用StopIteration 异常来终止。

        缺点:

        • Ruby 社区更讨厌它们,而不是使用异常进行控制流。

        【讨论】:

        • 为了 2011 年读者的利益:coffeepowered.net/2011/06/17/… 表示 JRuby 在异常方面仍然有些慢。
        • 我从未听说过 ruby​​ 社区讨厌 throw/catch(甚至是例外)的控制流。他们更喜欢延续吗?您是否阅读过关于 ruby​​-talk 或其他内容的这种情绪?
        【解决方案4】:
        while c1
         while c2
            do_break=true
         end
         next if do_break
        end
        

        或“break if do_break”取决于你想要什么

        【讨论】:

          【解决方案5】:

          不,没有。

          您的选择是:

          • 将循环放在一个方法中,并使用 return 从外部循环中断
          • 从内部循环设置或返回一个标志,然后在外部循环中检查该标志,并在设置标志时中断它(这有点麻烦)
          • 使用 throw/catch 跳出循环

          【讨论】:

          • 我喜欢第一个和第二个想法。非常干净的解决方案,不会滥用异常。
          【解决方案6】:

          考虑throw/catch。通常,下面代码中的外部循环将运行五次,但使用 throw 您可以将其更改为您喜欢的任何内容,并在此过程中将其中断。考虑一下这个完全有效的 ruby​​ 代码:

          catch (:done) do
            5.times { |i|
              5.times { |j|
                puts "#{i} #{j}"
                throw :done if i + j > 5
              }
            }
          end
          

          【讨论】:

          • 就我个人而言,我不喜欢在正常代码执行中使用异常引发。它迫使程序员遵循多个逻辑流。
          • 我不明白这条评论。在上面的代码 sn-p 中,任何地方都没有引发异常。整段代码只发送了6条消息:catchtimesputsthrow+<=>。我不会在任何地方发送raise
          • 我猜在 raise/rescue(异常)和 throw/catch 之间存在混淆,它们看起来很相似,但实际上并不相似。
          • "虽然 raise 和 rescue 的异常机制非常适合在出现问题时放弃执行,但在正常处理期间能够跳出一些深度嵌套的构造有时会很好。这就是 catch 和扔就派上用场了。” (ruby-doc.org/docs/ProgrammingRuby/html/tut_exceptions.html)
          • 这是一个在满足条件时使用 throw 退出长循环的示例:gist.github.com/1120376
          【解决方案7】:

          也许这就是你想要的? (未测试)

          stuff.find do |otherstuff|
            foo.find do
              somethingbad() && AAA
            end
          end
          

          find 方法一直循环,直到块返回非空值或命中列表末尾。

          【讨论】:

            【解决方案8】:

            我知道我早上会后悔,但只需使用 while 循环就可以了。

            x=0
            until x==10
              x+=1
              y=0
              until y==10
                y+=1
                if y==5 && x==3
                  x,y=10,10
                end
              end
              break if x==10
              puts x
            end
            

            if y==5 && x==3 只是表达式变为真的一个示例。

            【讨论】:

            • 现在是早上:请后悔。
            猜你喜欢
            • 1970-01-01
            • 2021-05-02
            • 2011-08-18
            • 2021-12-20
            • 2016-05-16
            • 2013-03-20
            • 1970-01-01
            • 2013-01-30
            • 2015-10-04
            相关资源
            最近更新 更多