【问题标题】:Is Throwing Exception to forcefully coming out of recursion efficient?抛出异常以强制退出递归有效吗?
【发布时间】:2019-03-31 07:49:51
【问题描述】:

以下只是伪代码。

  function x(node){
    if(node.val == 42) 
       return true; // What if I throw an exception here ?
    val1 = node.left ? x(node.left) : false ;
    val2 = node.right ? x(node.right): false; 
    return val1 || val2 ;
 }

假设我想在一棵树上找到 42。使用上面的代码,如果我返回一个有效值,它会冒泡到整个递归链,然后最终返回。

我的假设是,如果我只是抛出一个异常而不是在第 3 行返回 true,它实际上不会通过递归链冒泡并直接返回给调用者。

抛出异常来破坏整个链条是否是一种好习惯。因为假设在line 4,它确实在一些嵌套堆栈中找到了42,它仍然会在line 5 处进行递归。如果我只是在line 3 处抛出一个异常,我们可以避免这种不必要的计算。

问题更多的是编译器内部,如果我只是抛出一个未处理的异常,它是否仍然会冒泡到递归堆栈(这使得这种方法毫无意义)或直接返回到父调用的程序计数器。强>

【问题讨论】:

  • 一个学生问我这个..这个问题让我感到不安。
  • 如果您能描述为什么认为这是一个好主意,那将会很有用。我看不出它有什么好处,所以很难知道从哪里开始。
  • 应该使用异常来处理“异常”场景,因此用它们控制流程不是一个好主意。通常,可以使用更优雅的解决方案。你能发布一些代码来考虑吗?
  • 每当出现这种情况时,这是我的首选资源:softwareengineering.stackexchange.com/a/189225。请注意,关于抛出的昂贵“位”是调用本机 fillInStackTrace(),因此如果您确实想像这样爆发,请尽量避免这种情况。
  • 你为什么需要投掷?当你找到“一个有效的结果”时就返回,你永远不会递归太多。这就是重点:您通过与返回完全相同的堆栈推送次数返回

标签: java recursion tail-recursion


【解决方案1】:

出于几个不同的原因,我不建议抛出异常来中断递归链。

  • 效率。一般来说,抛出异常比从函数返回要慢得多。具体来说,当抛出异常时,运行时需要使用当前堆栈跟踪填充异常,需要查看要调用哪个处理程序,需要搜索要执行的 finally 块或 try-with-resources 语句,跟踪对象引用以进行垃圾收集等。这仍然需要遍历调用堆栈,就像在调用链中返回值一样,而且几乎肯定不会比更标准的方法更快。

  • 可用性。如果您抛出异常以中止递归链,那么调用您的函数的任何人都需要编写对该异常作出反应的代码。这意味着他们不需要像写if (myCall()) {...} 这样的事情,而是需要有单独的分支,一个用于调用正常返回值的地方,一个用于抛出的情况。如果他们忘记这样做,则运行时的代码可能会由于异常而出现故障,而实际上它实际上按预期运行。更糟糕的是,您实际上会反转异常的正常使用。如果您的代码在成功时抛出异常并在失败时返回一个值,那么阅读您的代码的人可能会“嗯?”在弄清楚你的意思之前至少一次。

  • 清晰度。异常应该用于非常特殊的情况!异常机制旨在在根本没有可行的方法返回值的情况下报告信息。将异常系统重新用于处理常规控制流会使代码更难阅读和维护,并且违反了最小意外原则。

虽然您最初的问题主要是关于效率点,但我认为其他两点 - 可用性和清晰度 - 仍然强烈支持不要以这种方式做事。

【讨论】:

  • 你的效率点如果为真则结束参数。如果编译器仍然需要遍历整个堆栈跟踪,那么这种方法确实没有意义。
  • @sapy 我认为即使效率更高,您仍然需要以某种方式克服其他障碍。对代码进行大量修改以提高效率会阻碍可读性,因此仍然需要进行成本效益分析。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2015-01-27
  • 1970-01-01
  • 2016-12-15
  • 2017-02-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多