V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
推荐关注
Meteor
JSLint - a JavaScript code quality tool
jsFiddle
D3.js
WebStorm
推荐书目
JavaScript 权威指南第 5 版
Closure: The Definitive Guide
rioshikelong121
V2EX  ›  JavaScript

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

  •  1
     
  •   rioshikelong121 · 2020-07-30 21:52:46 +08:00 · 4340 次点击
    这是一个创建于 1576 天前的主题,其中的信息可能已经有所发展或是发生改变。
    我最近写代码的过程中,通过抛出异常来结束掉已经找到结果的递归过程。这种做法推荐么?

    这样做可以清空 Call Stack 。
    19 条回复    2020-08-07 06:55:32 +08:00
    lxk11153
        1
    lxk11153  
       2020-07-30 22:20:59 +08:00
    难道不是 return 吗?
    Mistwave
        2
    Mistwave  
       2020-07-30 22:32:49 +08:00 via iPhone
    return
    BingoXuan
        3
    BingoXuan  
       2020-07-30 22:42:19 +08:00 via Android
    为什么要抛出异常? return 就是让你回到父函数,递归回去不就是回去开头吗?

    蛇尾即是蛇头
    rioshikelong121
        4
    rioshikelong121  
    OP
       2020-07-30 22:48:15 +08:00
    分支特别深的话,return 不是只能逐层往上返回么。这一部分的性能可以忽略?

    @lxk11153 @Mistwave @BingoXuan
    ipwx
        5
    ipwx  
       2020-07-30 23:10:38 +08:00
    @rioshikelong121 可是。。。异常也是一级一级返回上去的啊。不是你自己没有写这个逻辑,它就不存在了哇。

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

    楼主这异常运用灵活自如,想必以前是学 Python 的吧。
    wzzzx
        10
    wzzzx  
       2020-07-30 23:41:56 +08:00
    很明显,你写的递归有问题
    BingoXuan
        11
    BingoXuan  
       2020-07-30 23:46:30 +08:00 via Android
    @rioshikelong121
    你 debug 时候看到调用栈就是调试器一路往回捕获的结果。直接 return 就好了

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

    http://kangax.github.io/compat-table/es6/#test-proper_tail_calls_%28tail_call_optimisation%29
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   2632 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 22ms · UTC 10:57 · PVG 18:57 · LAX 02:57 · JFK 05:57
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.