如何突破红宝石块?

时间:2009-09-10 00:00:53

标签: ruby

以下是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中添加任何特定内容。

7 个答案:

答案 0 :(得分:707)

使用关键字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总是会导致封闭方法返回,无论它在块中嵌套的程度如何(除了lambda之外):

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

答案 1 :(得分:54)

我希望能够突破一个块 - 有点像转发,与循环无关。实际上,我想要在不终止循环的情况下中断循环中的块。为此,我使块成为一个迭代循环:

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

希望这有助于下一位根据主题界落在这里的Google员工。

答案 2 :(得分:37)

如果您希望您的块返回有用的值(例如,在使用#map#inject等时),nextbreak也接受参数。< / p>

请考虑以下事项:

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

答案 3 :(得分:19)

使用关键字break代替return

答案 4 :(得分:8)

也许您可以使用内置方法在数组中查找特定项目,而不是each - 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

答案 5 :(得分:2)

nextbreak似乎在这个简化的示例中做了正确的事情!

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

答案 6 :(得分:-2)

要从ruby块中突破,只需使用return关键字 return if value.nil?