如何从红宝石块中突围?


419

这里是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

Answers:


747

使用关键字 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

2
谢谢,但是next只移动到数组中的下一项。有可能退出吗?
user169930

接下来必须调用返回值。“ def f; x = yield;放x;结束”“ f做下3个;放” o“;结束”这将在控制台上打印3(但不显示“ o”)。
Marcel Jackwerth 09年

5
nextbreakreturn,你不能比较
finiteloop

我添加了一个答案,扩展了有关@MarcelJackwerth添加的关于使用nextbreak带有参数的评论。
Tyler Holien 2013年

8
还有一个名为的关键字redo,它基本上只是将执行移回到当前迭代中的块顶部。
Ajedi32

59

我希望能够突破一个障碍-有点像前进的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

希望这有助于根据主题行在此登陆的下一位googler。


4
这是回答该问题的唯一答复。应该得到更多的积分。谢谢。
亚历克斯·奈

中断和下一个的工作原理相同。如果将false更改为true,则next将保留外观,并且会中断。
G.艾伦·莫里斯三世

39

如果您希望块返回一个有用的值(例如,使用#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

1
感谢您提供有关参数的提示break
rkallensee

2
你能瓦特/一个具体的例子使用请更新break(可能只是取代你的一个nextbreak..
迈克·格拉夫

一件事很有趣。break something可以,break(something)但是会true && break(somehting)产生语法错误。仅供参考。如果需要条件,则需要使用ifunless
akostadinov


8

也许您可以使用内置方法在Array中查找特定项,而不是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
不要向Array类添加类似的任意方法!那真是不好的做法。
mrbrdo 2012年

3
Rails做到了,那他为什么不呢?
wberry 2012年

2
@wberry并不是说它一定。;)通常,除非有充分的理由,否则最好避免猴子补丁核心类(即添加一些非常有用的可泛化功能,而其他许多代码也会发现有用)。即使那样,也要轻轻松松,因为一旦一堂课被猴子大量修补,图书馆很容易开始彼此走动并引起一些极其奇怪的行为。
blm768

2

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


2
中断立即结束-下一个继续进行下一个迭代。
本·奥宾

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.