JS 中如何优雅的提前结束递归?

2020 年 7 月 30 日
 rioshikelong121
我最近写代码的过程中,通过抛出异常来结束掉已经找到结果的递归过程。这种做法推荐么?

这样做可以清空 Call Stack 。
5546 次点击
所在节点    JavaScript
19 条回复
lxk11153
2020 年 7 月 30 日
难道不是 return 吗?
Mistwave
2020 年 7 月 30 日
return
BingoXuan
2020 年 7 月 30 日
为什么要抛出异常? return 就是让你回到父函数,递归回去不就是回去开头吗?

蛇尾即是蛇头
rioshikelong121
2020 年 7 月 30 日
分支特别深的话,return 不是只能逐层往上返回么。这一部分的性能可以忽略?

@lxk11153 @Mistwave @BingoXuan
ipwx
2020 年 7 月 30 日
@rioshikelong121 可是。。。异常也是一级一级返回上去的啊。不是你自己没有写这个逻辑,它就不存在了哇。

而且异常还有比逐级 return 更多的东西需要处理。。。
ChanKc
2020 年 7 月 30 日
如果异常一下子就退掉整个栈……异常栈怎么办?你调递归的那个函数是不是也会被退出?
rioshikelong121
2020 年 7 月 30 日
@ChanKc 比如说。我写的函数 A 里面定义并且调用了 B 。B 递归调用自身并修改 A 闭包里面的变量。等到合适的时机抛出异常。A 里面 try catch 调用 B 。 我的理解是 Call Stack 里面只会清除掉由 B 递归调用产生的上下文。不会把 A 的调用也清掉。。。
rioshikelong121
2020 年 7 月 30 日
@ipwx 我还以为异常不需要逐层返回。而是会直接返回到捕获处或者顶层。。 如果是这样那确实没必要。
secondwtq
2020 年 7 月 30 日
一般是每次递归调用后检查一下是不是有结果了,有了就顺便返回。

楼主这异常运用灵活自如,想必以前是学 Python 的吧。
J55s5
2020 年 7 月 30 日
很明显,你写的递归有问题
BingoXuan
2020 年 7 月 30 日
@rioshikelong121
你 debug 时候看到调用栈就是调试器一路往回捕获的结果。直接 return 就好了

如果用 c 就可以很暴力用 goto 跳转了,但 js 和 Python 这些动态语言就不行了。
lsvih
2020 年 7 月 30 日
@secondwtq Python 也有异常堆栈 Traceback 的, 不背这个锅。。
secondwtq
2020 年 7 月 31 日
@lsvih 我说的是 Python 的 StopIteration
Austaras
2020 年 7 月 31 日
等一个独立自主发明 first class continuation, 看好你哟
ChanKc
2020 年 7 月 31 日
#7 那不是也要每一层去找 catch 吗
12tall
2020 年 7 月 31 日
@rioshikelong121 可以了解一下尾递归
lithbitren
2020 年 7 月 31 日
把递归用栈或队列存储存参数,然后改成迭代就可以提前结束了。
ipwx
2020 年 7 月 31 日
@rioshikelong121 你想一下啊,每个函数都有局部变量对吧。就算你没有 try ... catch ... ,局部变量的内存总得回收吧?调用栈的项目都得删掉吧?你 return 也不过就干了这两件事情吧?所以 throw 直接跳到顶层,只是写法上更省而已,执行过程不会省的。
chnwillliu
2020 年 8 月 7 日
难道不是依赖 js 引擎的尾调用优化? 可惜 chrome 貌似还没优化。

http://kangax.github.io/compat-table/es6/#test-proper_tail_calls_%28tail_call_optimisation%29

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://v2ex.ih06.com/t/694449

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX