Как вырваться из рубинового блока? - PullRequest
403 голосов
/ 10 сентября 2009

Вот 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.

Ответы [ 7 ]

715 голосов
/ 10 сентября 2009

Используйте ключевое слово 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
55 голосов
/ 12 апреля 2012

Я хотел просто иметь возможность вырваться из блока - что-то вроде прямого перехода, на самом деле не связанного с циклом. На самом деле, я хочу разорвать блок, находящийся в цикле, не прерывая цикл. Для этого я сделал блок одно-итерационным циклом:

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

Надеюсь, это поможет следующему гуглеру, который приземлится здесь в зависимости от темы.

37 голосов
/ 27 июля 2012

Если вы хотите, чтобы ваш блок возвращал полезное значение (например, при использовании #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
19 голосов
/ 10 сентября 2009

используйте ключевое слово break вместо return

8 голосов
/ 10 сентября 2009

Возможно, вы можете использовать встроенные методы для поиска определенных элементов в массиве вместо 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"

Один пример будет делать что-то вроде этого:

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 голосов
/ 29 мая 2014

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

0 голосов
/ 13 июня 2017

Чтобы вырваться из рубинового блока, просто используйте ключевое слово return return if value.nil?

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...