Как вырваться из рубинового блока?
Вот 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
Я думал об использовании raise, но я пытаюсь сделать его общим, поэтому я не хочу помещать что-либо конкретное в Foo
.
Ответы
Ответ 1
Используйте ключевое слово 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
всегда возвращает возвращающий метод, независимо от того, насколько глубоко он вложен в блоки (за исключением случая 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
Я хотел просто быть в состоянии вырваться из блока - вроде как прямое переключение, не связанное с циклом. На самом деле, я хочу разбить блок, который находится в цикле, не прерывая цикл. Для этого я сделал блок одноэлементным циклом:
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
Надеюсь, что это поможет следующему гуглеру, который приземляется здесь на основе темы.
Ответ 3
Если вы хотите, чтобы ваш блок возвращал полезное значение (например, при использовании #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
Ответ 4
используйте ключевое слово break
вместо return
Ответ 5
Возможно, вы можете использовать встроенные методы для поиска определенных элементов в массиве вместо 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
Ответ 6
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
Ответ 7
Для того, чтобы выйти из рубинового блока просто использовать return
ключевое слово return if value.nil?
, return if value.nil?