下面是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中放任何特定的东西。
使用关键字next。如果你不想继续下一个项目,使用休息。
当在块中使用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在block中的用法:
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
也许您可以使用内置的方法来查找数组中的特定项,而不是逐个查找目标并手动完成所有操作。举几个例子:
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
如果你想让你的块返回一个有用的值(例如,当使用#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
你有四种方法以“非异常”方式展开堆栈:next、break、return和throw。
Next将导致块返回。
Break将导致yield给该块的方法返回。
Return将导致定义该块的方法返回。
Throw将遍历堆栈,直到找到具有匹配符号的catch,并使其返回。这很像非异常情况的“轻量级”异常。
它们都可以接受一个返回值,这个返回值将由它们所导致的返回值返回,而不是它们上一个表达式通常返回的值。
下面是一些例子:
def doSomething
puts "> doSomething"
yield
puts "< doSomething"
end
def withNext
puts "> withNext"
doSomething do
puts "> block"
puts "* NEXT! causes the block to return to doSomething"
next
puts "< block"
end
puts "< withNext"
end
def withBreak
puts "> withBreak"
doSomething do
puts "> block"
puts "* BREAK! causes doSomething to return to withBreak"
break
puts "< block"
end
puts "< withBreak"
end
def withReturn
puts "> withReturn"
doSomething do
puts "> block"
puts "* RETURN! causes withReturn to return"
return
puts "< block"
end
puts "< withReturn"
end
def withThrow
puts "> withThrow"
catch :label do
puts "> catch :label"
doSomething do
puts "> block 1"
doSomething do
puts "> block 2"
puts "* THROW! causes catch :label to return to withThrow"
throw :label
puts "< block 2"
end
puts "< block 1"
end
puts "< catch :label"
end
puts "< withThrow"
end
withNext
puts "* Done"
puts
withBreak
puts "* Done"
puts
withReturn
puts "* Done"
puts
withThrow
puts "* Done"
输出:
> withNext
> doSomething
> block
* NEXT! causes the block to return to doSomething
< doSomething
< withNext
* Done
> withBreak
> doSomething
> block
* BREAK! causes doSomething to return to withBreak
< withBreak
* Done
> withReturn
> doSomething
> block
* RETURN! causes withReturn to return
* Done
> withThrow
> catch :label
> doSomething
> block 1
> doSomething
> block 2
* THROW! causes catch :label to return to withThrow
< withThrow
* Done