ルビーブロックから抜け出す方法は?


420

ここにありBar#do_thingsます:

class Bar   
  def do_things
    Foo.some_method(x) do |x|
      y = x.do_something
      return y_is_bad if y.bad? # how do i tell it to stop and return do_things? 
      y.do_something_else
    end
    keep_doing_more_things
  end
end

そしてここにありFoo#some_methodます:

class Foo
  def self.some_method(targets, &block)
    targets.each do |target|
      begin
        r = yield(target)
      rescue 
        failed << target
      end
    end
  end
end

レイズを使用することを考えましたが、私はそれをジェネリックにしようとしているので、何も特定のものにしたくありませんFoo

回答:


747

キーワードを使用し nextます。次の項目に進みたくない場合は、を使用してくださいbreak

場合nextブロック内で使用され、それは再びブロックを呼び出すことによって新たな反復を開始することができるイテレータ方法、に制御を戻し、直ちに出口にブロックを引き起こします。

f.each do |line|              # Iterate over the lines in file f
  next if line[0,1] == "#"    # If this line is a comment, go to the next
  puts eval(line)
end

ブロックで使用される場合、ブロックbreakから、ブロックを呼び出したイテレータから、そしてイテレータの呼び出しに続く最初の式に制御を移します。

f.each do |line|             # Iterate over the lines in file f
  break if line == "quit\n"  # If this break statement is executed...
  puts eval(line)
end
puts "Good bye"              # ...then control is transferred here

そして最後returnに、ブロックでの使用:

return (ラムダの場合を除いて)ブロック内のネストの深さに関係なく、常に囲みメソッドを返します。

def find(array, target)
  array.each_with_index do |element,index|
    return index if (element == target)  # return from find
  end
  nil  # If we didn't find the element, return nil
end

2
おかげで、次は配列の次の項目に移動するだけです。終了することは可能ですか?
user169930 2009

次に、戻り値を指定して呼び出す必要があります。"def f; x = yield; puts x; end" "f do next 3; puts" o "; end"これは、コンソールに3( "o"はなし)を出力します。
マルセルジャックウェルス2009

5
nextbreakreturn、あなたは比較することはできません
finiteloop

@MarcelJackwerthの使用について、nextまたはbreak引数を使用して追加されたコメントを拡張して、回答を追加しました。
Tyler Holien 2013年

8
と呼ばれるキーワードもありますredo。これは基本的に、現在の反復内で実行をブロックの先頭に戻すだけです。
Ajedi32 2014年

59

私はブロックから抜け出すことができるようにしたかっただけです-ループとは関係なく、転送後藤のようなものです。実際、ループを終了せずにループ内のブロックを中断したいのですが。そのために、ブロックを1回の繰り返しループにしました。

for b in 1..2 do
    puts b
    begin
        puts 'want this to run'
        break
        puts 'but not this'
    end while false
    puts 'also want this to run'
end

これが件名に基づいてここに着く次のグーグルを助けることを願っています。


4
これが質問が出されたときに回答した唯一の回答でした。より多くのポイントに値する。ありがとう。
Alex Nye

これは、breakとnextの両方で同じように機能します。falseがtrueに変更された場合、nextは外観にとどまり、breakが発生します。
G.アレンモリスIII

39

あなたのブロックが便利値(例えば使用する際戻したい場合#map#injectなど)を、nextそしてbreakまた引数を受け入れます。

以下を検討してください。

def contrived_example(numbers)
  numbers.inject(0) do |count, x|
    if x % 3 == 0
      count + 2
    elsif x.odd?
      count + 1
    else 
      count
    end
  end
end

同等の使用next

def contrived_example(numbers)
  numbers.inject(0) do |count, x|
    next count if x.even?
    next (count + 2) if x % 3 == 0
    count + 1
  end
end

もちろん、必要なロジックを常にメソッドに抽出し、それをブロック内から呼び出すこともできます。

def contrived_example(numbers)
  numbers.inject(0) { |count, x| count + extracted_logic(x) }
end

def extracted_logic(x)
  return 0 if x.even?
  return 2 if x % 3 == 0
  1
end

1
の引数を含むヒントをありがとうbreak
rkallensee 14

2
あなたは使用してワット/具体的な例更新を喜ばことができbreak(おそらくちょうどあなたの1置き換えるnextbreak..を
マイク・グラーフ

非常に興味深いことが1つあります。break something機能しますbreak(something)が機能しtrue && break(somehting)ますが、構文エラーが発生します。参考までに。条件が必要な場合、ifまたはunless使用する必要があります。
akostadinov 2016年


8

おそらく、あなたは代わりの配列で特定のアイテムを見つけるための組み込みメソッドを使用することができますeach-ing targetsと手ですべてをやって。いくつかの例:

class Array
  def first_frog
    detect {|i| i =~ /frog/ }
  end

  def last_frog
    select {|i| i =~ /frog/ }.last
  end
end

p ["dog", "cat", "godzilla", "dogfrog", "woot", "catfrog"].first_frog
# => "dogfrog"
p ["hats", "coats"].first_frog
# => nil
p ["houses", "frogcars", "bottles", "superfrogs"].last_frog
# => "superfrogs"

1つの例は次のようなことです。

class Bar
  def do_things
    Foo.some_method(x) do |i|
      # only valid `targets` here, yay.
    end
  end
end

class Foo
  def self.failed
    @failed ||= []
  end

  def self.some_method(targets, &block)
    targets.reject {|t| t.do_something.bad? }.each(&block)
  end
end

2
そのような任意のメソッドをArrayクラスに追加しないでください!それは本当に悪い習慣です。
mrbrdo 2012年

3
Railsはそれを行うので、なぜ彼はできないのですか?
wberry

2
@wberry必ずしもそうあるべきだと言っているのではありません。;)ただし、一般的には、かなりの理由がない限り(つまり、他の多くのコードで役立つと思われる非常に便利な一般化可能な機能を追加する場合を除いて)、コアクラスにモンキーパッチを適用しないことをお勧めします。それでも、クラスが大幅にサルにパッチされると、ライブラリがお互いの上を歩き始め、極端に奇妙な動作を引き起こしやすくなるため、軽く踏みます。
blm768 2016年

2

nextそしてbreak、この簡単な例では、正しいことを行うように見えます!

class Bar
  def self.do_things
      Foo.some_method(1..10) do |x|
            next if x == 2
            break if x == 9
            print "#{x} "
      end
  end
end

class Foo
    def self.some_method(targets, &block)
      targets.each do |target|
        begin
          r = yield(target)
        rescue  => x
          puts "rescue #{x}"
        end
     end
   end
end

Bar.do_things

出力:1 3 4 5 6 7 8


2
ブレークはすぐに終了します-次は次の反復に進みます。
Ben Aubin 2016年

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.