Làm thế nào để thoát ra khỏi một khối ruby?


420

Đây là 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

Và đây là 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

Tôi đã nghĩ về việc sử dụng tăng, nhưng tôi đang cố gắng làm cho nó chung chung, vì vậy tôi không muốn đưa bất cứ điều gì cụ thể vào Foo.

Câu trả lời:


747

Sử dụng từ khóa next. Nếu bạn không muốn tiếp tục mục tiếp theo, hãy sử dụng break.

Khi nextđược sử dụng trong một khối, nó làm cho khối thoát ra ngay lập tức, trả lại quyền điều khiển cho phương thức lặp, sau đó có thể bắt đầu một lần lặp mới bằng cách gọi lại khối:

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

Khi được sử dụng trong một khối, breakchuyển điều khiển ra khỏi khối, ra khỏi trình vòng lặp đã gọi khối đó và đến biểu thức đầu tiên theo lệnh gọi của trình vòng lặp:

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

Và cuối cùng, việc sử dụng returntrong một khối:

return luôn luôn khiến phương thức kèm theo quay trở lại, bất kể nó được lồng sâu đến mức nào trong các khối (trừ trường hợp 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

2
cảm ơn, nhưng tiếp theo chỉ di chuyển đến mục tiếp theo trong mảng. có thể thoát ra không?
dùng169930

Bạn phải gọi tiếp theo với giá trị trả về. "def f; x = ield
Marcel Jackwerth

5
next, break, return, Bạn không thể so sánh
finiteloop

Tôi đã thêm một câu trả lời mở rộng trên nhận xét @MarcelJackwerth đã thêm về việc sử dụng nexthoặc breakvới một đối số.
Tyler Holien

8
Ngoài ra còn có một từ khóa được gọi redo, về cơ bản chỉ cần di chuyển thực thi trở lại đầu khối trong vòng lặp hiện tại.
Ajedi32

59

Tôi muốn chỉ có thể thoát ra khỏi một khối - giống như một goto phía trước, không thực sự liên quan đến một vòng lặp. Trong thực tế, tôi muốn phá vỡ một khối trong một vòng lặp mà không kết thúc vòng lặp. Để làm điều đó, tôi đã tạo cho khối một vòng lặp một lần lặp:

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

Hy vọng điều này sẽ giúp googler tiếp theo hạ cánh ở đây dựa trên dòng chủ đề.


4
Đây là câu trả lời duy nhất trả lời câu hỏi khi nó được đặt. Xứng đáng hơn điểm. Cảm ơn.
Alex Nye

Điều này hoạt động như nhau cho cả phá vỡ và tiếp theo. Nếu sai được thay đổi thành đúng thì tiếp theo sẽ ở trong giao diện và phá vỡ sẽ thoát ra.
G. Allen Morris III

39

Nếu bạn muốn block của bạn để trả về một giá trị hữu ích (ví dụ như khi sử dụng #map, #injectvv), nextbreakcũng chấp nhận một cuộc tranh cãi.

Hãy xem xét những điều sau đây:

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

Tương đương với việc sử dụng 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

Tất nhiên, bạn luôn có thể trích xuất logic cần thiết vào một phương thức và gọi nó từ bên trong khối của bạn:

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
Cảm ơn các gợi ý với các đối số cho break!
rkallensee

2
bạn có thể vui lòng cập nhật w / một ví dụ cụ thể bằng cách sử dụng break(có thể chỉ cần thay thế một trong số bạn nextbằng break..
Mike Graf

Một điều rất thú vị. break somethinghoạt động, break(something)hoạt động nhưng true && break(somehting)mang lại một lỗi cú pháp. Chỉ cần FYI. Nếu điều kiện là cần thiết, sau đó ifhoặc unlesscần phải được sử dụng.
akostadinov


8

Có lẽ bạn có thể sử dụng các phương thức tích hợp sẵn để tìm các mục cụ thể trong Mảng, thay vì each-ing targetsvà làm mọi thứ bằng tay. Một vài ví dụ:

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"

Một ví dụ sẽ làm một cái gì đó như thế này:

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
Đừng thêm các phương thức tùy ý như thế vào lớp Array! Đó là thực tế xấu.
mrbrdo

3
Rails làm điều đó, vậy tại sao anh ta không thể?
dâu

2
@wberry Điều đó không có nghĩa là nó nhất thiết phải như vậy . ;) Nói chung, mặc dù vậy, tốt nhất là tránh khỉ vá các lớp lõi trừ khi bạn có một lý do khá chính đáng (nghĩa là thêm một số chức năng khái quát rất hữu ích mà nhiều mã khác sẽ thấy hữu ích). Ngay cả sau đó, bước đi nhẹ nhàng bởi vì một khi một lớp học bị vá rất nhiều, các thư viện dễ dàng bắt đầu đi ngang qua nhau và gây ra một số hành vi cực kỳ kỳ quặc.
blm768

2

nextbreakdường như làm điều đúng trong ví dụ đơn giản này!

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

đầu ra: 1 3 4 5 6 7 8


2
ngắt kết thúc ngay lập tức - tiếp theo tiếp tục lặp lại tiếp theo.
Ben Aubin

-3

Để thoát ra khỏi khối ruby, chỉ cần sử dụng returntừ khóa return if value.nil?


2
Không returnthoát khỏi chức năng?
ragerdl
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.