【问题标题】:What is catch and throw used for in Ruby?Ruby 中的 catch 和 throw 有什么用?
【发布时间】:2011-04-12 14:54:53
【问题描述】:

在大多数其他语言中,catch 和 throw 语句的作用与 Ruby 中的 begin、rescue 和 raise 语句相同。我知道你可以用这两个语句做到这一点:

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

if some_condition
  throw :done
end

但这有什么用呢?谁能给我一个例子,说明 Ruby 中的 catch 和 throw 语句是做什么用的?

【问题讨论】:

标签: ruby


【解决方案1】:

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

在 Sinatra 中,您可以随时使用haltimmediately terminate a request

halt

您也可以指定停止时的状态...

halt 410

或者身体……

halt 'this will be the body'

或两者兼而有之……

halt 401, 'go away!'

停止方法是implemented using throw

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

caught by invoke 方法。

:halt 在 Sinatra 中有多种用途。您可以阅读源代码以获取更多示例。

【讨论】:

  • 抛出这是一种更优雅的方式来使用类异常系统作为控制流。
  • @jsz 第一,它要快得多——堆栈帧不必沿着“抛出的符号”携带,也不会创建任何对象。轻量级非线性流量控制。
  • @mezis 是的现代 GOTO
【解决方案2】:

在使用递归函数编写作用于嵌套数据结构的递归算法时,您可以使用throw,类似于使用@987654324 编写作用于平面数据的迭代算法时使用break 或早期return @循环。

例如,假设您有一个正整数列表,并且您希望(出于某种原因)编写一个函数,如果满足以下任一条件,该函数将返回 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 中,有!假设您有一个 tree 并想要检查其 叶子 是否包含5 或总和超过 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 调用下方的整个调用堆栈。在其他语言中,您可以通过为此目的滥用异常或使用一些返回码来告诉递归函数停止递归来做到这一点,但这更直接和更简单。

【讨论】:

    【解决方案3】:

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

    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 解决了一个欧拉问题。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2013-11-24
      • 2013-01-18
      • 2012-06-04
      • 1970-01-01
      • 1970-01-01
      • 2014-03-18
      • 2010-12-14
      相关资源
      最近更新 更多