抛出异常以强制从递归中出来是否有效?



以下只是伪代码。

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抛出一个异常,我们可以避免不必要的计算。

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

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

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

  • 可用性。如果抛出异常来中止递归链,则调用函数的人都需要编写对该异常做出反应的代码。这意味着它们需要有单独的分支,if (myCall()) {...}一个用于调用正常返回值的情况,一个用于调用正常返回值的情况,另一个用于调用抛出的情况。如果他们忘记这样做,运行时的代码可能会由于异常而失败,而实际上它实际上的行为符合预期。更糟糕的是,您实际上会颠倒异常的正常使用。如果你的代码在成功时抛出异常,失败时返回一个值,那么无论谁读你的代码,在弄清楚你的意思之前,都可能会至少"嗯?"一次。

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

虽然你最初的问题主要是关于效率点,但我认为另外两点 - 可用性和清晰度 - 仍然强烈支持不这样做。

最新更新