web-dev-qa-db-ja.com

Rubyで外側のサイクルを壊す方法は?

Perlには、次のような外部サイクルを中断する機能があります。

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

(構文が間違っている可能性があります)、ループラベルを使用して、外側のループを内側のループの内側から切り離します。 Rubyに似たものはありますか?

53
Fluffy

必要なのは、非ローカルの制御フローです。これは、Rubyにいくつかのオプションがあります。

  • 継続、
  • 例外、および
  • throw/catch

継続

長所:

  • 継続は、非ローカル制御フローの標準メカニズムです。実際、any非ローカル制御フロー(サブルーチン、プロシージャ、関数、メソッド、コルーチン、ステートマシン、ジェネレータ、条件、例外)を構築できます)それらの上に:GOTOのより良い双子です。

短所:

  • 継続はRuby言語仕様の必須部分ではありません。つまり、一部の実装(XRuby、JRuby、Ruby.NET、IronRuby)はそれらを実装していません。したがって、信頼できません。それらの上に。

例外

長所:

  • 例外は継続よりも強力である可能性があることを数学的に証明する論文があります。 IOW:継続が実行できるすべてのことを実行できるため、継続の代わりとして使用できます。
  • 例外は世界中で利用できます。

短所:

  • それらは「例外」と呼ばれ、「例外的な状況でのみ」と人々に思わせます。これは3つのことを意味します:コードを読んでいる誰かが理解できない、実装が最適化されていない可能性があります(そして、はい、例外areほぼすべての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コミュニティーは、制御フローに例外を使用するよりもさらに嫌いです。
36
Jörg W Mittag

throw/catch を検討してください。通常、以下のコードの外側のループは5回実行されますが、throwを使用すると、ループを好きなように変更して、プロセスを中断できます。この完全に有効なRubyコードを検討してください:

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

いいえ、ありません。

オプションは次のとおりです。

  • ループをメソッドに入れ、リターンを使用して外側のループから抜けます
  • 内側のループからフラグを設定または返す。次に、外側のループでそのフラグをチェックし、フラグが設定されたときにフラグを解除する(これはちょっと面倒です)。
  • ループから抜け出すためにスロー/キャッチを使用する
30
sepp2k
while c1
 while c2
    do_break=true
 end
 next if do_break
end

または、必要に応じて「do_breakの場合はブレーク」

4
Anno2001

たぶん、これはあなたが望むものですか? (未検証)

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

Findメソッドは、ブロックがnull以外の値を返すか、リストの最後に到達するまでループを続けます。

2
Jeff Waltzer

私は午前中にこれを後悔することを知っていますが、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は、式がtrueになる例にすぎません。

0
Jonas Elfström

内部メソッドをループの周りにラップすると、トリックを実行できます例:

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ループ内でチェックを行い、条件が満たされたときに内部メソッドから戻ることができます。

0
Metareven

外側のループを制御するために、内側のループの内側に設定されるフラグを追加することを検討できます。

外側のループを「次へ」

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
0
Stephen LAI