Bagaimana cara keluar dari blok ruby?

420

Ini adalah 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

Dan di sini adalah 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

Saya berpikir tentang menggunakan kenaikan gaji, tetapi saya mencoba membuatnya generik, jadi saya tidak ingin memasukkan sesuatu yang spesifik Foo.

pengguna169930
sumber

Jawaban:

747

Gunakan kata kunci next. Jika Anda tidak ingin melanjutkan ke item berikutnya, gunakan break.

Ketika nextdigunakan di dalam blok, itu menyebabkan blok untuk segera keluar, mengembalikan kontrol ke metode iterator, yang kemudian dapat memulai iterasi baru dengan memohon blok lagi:

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

Saat digunakan dalam sebuah blok, breaktransfer kontrol keluar dari blok, keluar dari iterator yang memanggil blok, dan ke ekspresi pertama setelah doa iterator:

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

Dan akhirnya, penggunaan returndalam blok:

return selalu menyebabkan metode penutup untuk kembali, terlepas dari seberapa dalam bersarang di dalam blok itu (kecuali dalam kasus lambdas):

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
JRL
sumber
2
terima kasih, tetapi selanjutnya hanya bergerak ke item berikutnya dalam array. apakah mungkin untuk keluar?
user169930
Anda harus menelepon selanjutnya dengan nilai pengembalian. "def f; x = yield; menempatkan x; end" "f lakukan 3 berikutnya; menempatkan" o "; end" Ini mencetak 3 (tetapi tidak ada "o") di konsol.
Marcel Jackwerth
5
next, break, return, Anda tidak bisa membandingkan
finiteloop
Saya menambahkan jawaban yang berkembang di komentar @MarcelJackwerth menambahkan tentang menggunakan nextatau breakdengan argumen.
Tyler Holien
8
Ada juga kata kunci yang disebut redo, yang pada dasarnya hanya memindahkan eksekusi kembali ke atas blok dalam iterasi saat ini.
Ajedi32
59

Saya hanya ingin bisa keluar dari blok - semacam goto ke depan, tidak benar-benar terkait dengan loop. Bahkan, saya ingin memecah blok yang ada dalam satu lingkaran tanpa mengakhiri loop. Untuk melakukan itu, saya membuat blok loop satu iterasi:

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

Semoga ini bisa membantu googler berikutnya yang mendarat di sini berdasarkan baris subjek.

Don Law
sumber
4
Ini adalah satu-satunya jawaban yang menjawab pertanyaan yang diajukan. Pantas mendapatkan lebih banyak poin. Terima kasih.
Alex Nye
Ini berfungsi sama untuk istirahat dan berikutnya. Jika false diubah menjadi true maka selanjutnya akan tetap dalam tampilan dan break akan pecah.
G. Allen Morris III
39

Jika Anda ingin blok Anda untuk mengembalikan nilai yang berguna (misalnya saat menggunakan #map, #inject, dll), nextdanbreak juga menerima argumen.

Pertimbangkan yang berikut ini:

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

Setara menggunakan 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

Tentu saja, Anda selalu dapat mengekstrak logika yang diperlukan ke dalam metode dan menyebutnya dari dalam blok Anda:

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
Tyler Holien
sumber
1
Terima kasih atas petunjuknya untuk break!
rkallensee
2
bisa tolong perbarui dengan contoh khusus menggunakan break(mungkin hanya mengganti salah satu dari Anda nextdengan break..
Mike Graf
Satu hal yang sangat menarik. break somethingberfungsi, break(something)bekerja tetapi true && break(somehting)menghasilkan kesalahan sintaksis. Hanya FYI. Jika kondisi diperlukan, maka ifatau unlessperlu digunakan.
akostadinov
21

gunakan kata kunci breakalih-alihreturn

ASHelly
sumber
8

Mungkin Anda dapat menggunakan built-in metode untuk menemukan item tertentu dalam Array, bukan each-ing targetsdan melakukan segala sesuatu dengan tangan. Beberapa contoh:

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"

Salah satu contoh akan melakukan sesuatu seperti ini:

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
Lilleaas Agustus
sumber
2
Jangan menambahkan metode arbitrer seperti itu ke kelas Array! Itu praktik yang sangat buruk.
mrbrdo
3
Rails melakukannya, jadi mengapa dia tidak bisa?
wberry
2
@wberry Itu tidak berarti bahwa itu seharusnya . ;) Namun, secara umum, yang terbaik adalah menghindari kelas inti penambalan monyet kecuali Anda memiliki alasan yang cukup bagus (yaitu menambahkan beberapa fungsi yang dapat digeneralisasikan yang sangat berguna yang akan berguna bagi banyak kode lainnya). Meski begitu, menginjak ringan karena sekali kelas sangat ditambal monyet, mudah bagi perpustakaan untuk mulai berjalan satu sama lain dan menyebabkan beberapa perilaku yang sangat aneh.
blm768
2

nextdan breaktampaknya melakukan hal yang benar dalam contoh sederhana ini!

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

output: 1 3 4 5 6 7 8

G. Allen Morris III
sumber
2
istirahat segera berakhir - selanjutnya berlanjut ke iterasi berikutnya.
Ben Aubin
-3

Untuk keluar dari blok ruby ​​cukup gunakan returnkata kunci return if value.nil?

Kiry Meas
sumber
2
Tidak returnkeluar dari fungsi?
ragerdl