如何在Ruby中打破外循环?

Flu*_*ffy 54 ruby loops cycle

在Perl中,有能力打破这样的外部循环:

AAA: for my $stuff (@otherstuff) {
         for my $foo (@bar) {
             last AAA if (somethingbad());
         }
      }
Run Code Online (Sandbox Code Playgroud)

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

Chr*_*nch 106

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

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

  • 我猜在加注/救援(例外)和投掷/捕获之间存在混淆,它们看起来相似但不是. (20认同)
  • 我不明白这个评论.在上面的代码段中,没有任何异常.在整段代码中只发送了六条消息:`catch`,`times`,`puts`,`throw`,`+`和`<=>`.我不在任何地方发送`raise`. (18认同)
  • "虽然提升和救援的异常机制非常适合在出现问题时放弃执行,但有时候能够在正常处理过程中跳出一些深度嵌套的构造是很好的.这就是捕捉和投掷派上用场的好处." (http://ruby-doc.org/docs/ProgrammingRuby/html/tut_exceptions.html) (9认同)
  • 就个人而言,我不喜欢使用异常提升来执行正常的代码.它迫使程序员遵循多个逻辑流程. (5认同)

Jör*_*tag 36

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

  • 延续,
  • 例外,和
  • throw/catch

延续

优点:

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

缺点:

  • Continuations不是Ruby语言规范的强制性部分,这意味着某些实现(XRuby,JRuby,Ruby.NET,IronRuby)不实现它们.所以,你不能依赖它们.

例外

优点:

  • 有一篇论文在数学上证明了Exceptions可以比Continuations更强大.IOW:他们可以做延续可以做的所有事情,甚至更多,所以你可以用它们来代替延续.
  • 普遍存在例外情况.

缺点:

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

throw/catch

这是(粗略地)它的样子:

catch :aaa do
  stuff.each do |otherstuff|
    foo.each do |bar|
      throw :aaa if somethingbad
    end
  end
end
Run Code Online (Sandbox Code Playgroud)

优点:

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

缺点:

  • Ruby社区比使用控制流的异常更讨厌它们.

  • 我从来没有听说过ruby社区讨厌控制流的'throw` /`catch`(甚至例外).他们喜欢延续吗?你有没有读过关于ruby-talk之类的这种观点? (2认同)

sep*_*p2k 30

不,没有.

你的选择是:

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