什么是Ruby中的catch和throw?

时间:2010-09-15 10:38:21

标签: ruby

在大多数其他语言中,catch和throw语句执行begin,rescue和raise语句在Ruby中执行的操作。我知道你可以用这两个陈述来做到这一点:

catch :done do
  puts "I'm done."
end

if some_condition
  throw :done
end

但这有用吗?有人可以给我一个关于Ruby中使用catch和throw语句的例子吗?

3 个答案:

答案 0 :(得分:34)

您可以使用它来打破嵌套循环。

INFINITY = 1.0 / 0.0
catch (:done) do
  1.upto(INFINITY) do |i|
    1.upto(INFINITY) do |j|
      if some_condition
        throw :done
      end
    end
  end
end

如果您使用了上面的break语句,那么它就会破坏内部循环。但是如果你想要打破嵌套循环,那么这个catch / throw会非常有用。我用here来解决欧拉问题之一。

答案 1 :(得分:21)

我一直在寻找一个很好的例子,直到我遇到Sinatra。 恕我直言,Sinatra为catch公开了一个非常有趣的示例用法。

在Sinatra,您可以halt随时使用halt

halt 410

您还可以在暂停时指定状态...

halt 'this will be the body'

或身体......

halt 401, 'go away!'

或两者......

def halt(*response)
  response = response.first if response.length == 1
  throw :halt, response
end

暂停方法为immediately terminate a request

invoke

implemented using throw :halt方法。

Sinatra中有{{1}}的几种用法。您可以阅读源代码以获取更多示例。

答案 2 :(得分:1)

在编写使用递归函数作用于嵌套数据结构的递归算法时,您可以使用throw类似于在编写行为的迭代算法时使用break或早期return的方式使用for循环对平面数据进行处理。

例如,假设您有一个正整数列表,并且您希望(出于某种原因)编写一个函数,如果满足以下任一条件,该函数将返回true:

  • 列表中所有元素的总和大于100
  • 列表中的某些元素(如果等于5

让我们说你总是希望在列表中的单个短路传递中执行此检查,而不是进行reduce调用以获得总和以及单独的any?调用寻找五个人。

你可能会写一些类似的代码(事实上,你可能已经在某些语言中用这种代码编写了这样的代码):

def test(list)
  sum = 0
  for i in list
    sum += i
    if i == 5 || sum > 100
      return true
    end
  end
  return false
end

在大多数语言中,没有干净的等价物来突破使用递归函数的递归算法。但是在Ruby中,有!假设,有一个而不是拥有一个列表并且想要检查其元素是否包含五个或总和超过100,并且想要检查它的离开是否包含一旦你知道答案就会发生五次或者一次超过100次的短路和返回。

您可以使用throw / catch优雅地执行此操作,如下所示:

def _test_recurse(sum_so_far, node)
  if node.is_a? InternalNode
    for child_node in node.children
      sum_so_far = _test_recurse(sum_so_far, child_node)
    end
    return sum_so_far
  else # node.is_a? Leaf
    sum_so_far += node.value
    if node.value == 5
      throw :passes_test
    elsif sum_so_far > 100
      throw :passes_test
    else
      return sum_so_far
    end
  end
end

def test(tree)            
  catch (:passes_test) do
    _test_recurse(0, tree)
    return false
  end
  return true
end

这里的throw :passes_test有点像break;它可以让你跳出最外面的_test电话下面的整个调用堆栈。在其他语言中,您可以通过为此目的滥用异常或使用一些返回代码来告诉递归函数停止递归来实现此目的,但这更直接,更简单。

相关问题