如何突破红宝石块?

use*_*930 407 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
Run Code Online (Sandbox Code Playgroud)

这是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
Run Code Online (Sandbox Code Playgroud)

我想过使用raise,但我试图让它变得通用,所以我不想把任何具体的东西放进去Foo.

JRL*_*JRL 730

使用关键字 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
Run Code Online (Sandbox Code Playgroud)

在块中使用时,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
Run Code Online (Sandbox Code Playgroud)

最后,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
Run Code Online (Sandbox Code Playgroud)

  • 还有一个名为`redo`的关键字,它基本上只是将执行移回到当前迭代中的块顶部. (8认同)
  • `next`,`break`,`return`,你无法比较 (5认同)
  • 谢谢,但接下来只移动到数组中的下一个项目.有可能退出吗? (2认同)

小智 56

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

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
Run Code Online (Sandbox Code Playgroud)

希望这有助于下一个根据主题线在这里登陆的googler.

  • 这是回答这个问题的唯一回应.值得多点.谢谢. (4认同)

Tyl*_*ien 38

如果你希望你的块返回一个有用的值(例如,当使用#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
Run Code Online (Sandbox Code Playgroud)

等效使用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
Run Code Online (Sandbox Code Playgroud)

当然,您总是可以在方法中提取所需的逻辑并从块内部调用它:

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
Run Code Online (Sandbox Code Playgroud)

  • 你可以使用`break`更新w /一个具体的例子(可能只是用`break`替换你的`next`之一. (2认同)

ASh*_*lly 19

使用关键字break代替return


Aug*_*aas 8

也许你可以使用内置的方法在阵列发现的特定项目,而不是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"
Run Code Online (Sandbox Code Playgroud)

一个例子是做这样的事情:

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
Run Code Online (Sandbox Code Playgroud)

  • Rails做到了,为什么他不能呢? (3认同)
  • 不要向Array类添加这样的任意方法!这真是糟糕的做法. (2认同)
  • @wberry这并不是说它必然是_should_.;)一般来说,最好避免猴子修补核心类,除非你有一个很好的理由(即添加一些非常有用的_generalizable_功能,许多其他代码将发现有用).即使这样,也要轻描淡写,因为一旦一个班级经过大量的猴子修补,图书馆就很容易开始互相走动并造成一些非常奇怪的行为. (2认同)