Fer*_*osd 3 javascript arrays recursion loops
所以,我已经理解了JavaScript 中递归的概念(有一个函数可以循环自身,直到达到基本条件,此时它停止并返回最终结果),但我有一点当我将其应用到尝试应用它来创建数组的实际语法时,我感到头疼。
\n让我们使用freeCodeCamp 的倒计时练习作为示例。这是正确的结果:
\nfunction countdown(n) {\n if (n < 1) {\n return [];\n } else {\n const arr = countdown(n - 1);\n arr.unshift(n);\n return arr;\n }\n}\nRun Code Online (Sandbox Code Playgroud)\n这里的想法显然是有一个函数从给定的n倒数到 1。我理解这个想法,但我有几个问题:
\n1 -为什么将数组声明为常量并分配给递归调用?数组不应该更好地在外部声明为变量(因为它会更改值)\xe2\x80\x93 并且其当前位置不意味着每次发生递归时都会声明(并覆盖)它?
\n2 -我得到了当前值的不变,但为什么它是在递归调用之后声明的?难道它不应该放置在之前(上面一行)以便在函数循环本身之前取消移位值吗?
\n3 -同样,为什么在 else 循环中返回完整的最终数组?难道不应该更好地在外部返回,或者在上面的基本条件上返回,以免每次函数循环时都返回吗?
\n事实上,我的理解是,当n达到 0 时,循环应该返回一个空数组,而不是通过递归创建的完整数组。
\n~~~
\n最终编辑/理解:感谢每一个答案!因此,如果我目前的理解是正确的,递归不会循环该函数,正如我最初认为 \xe2\x80\x93 相反,它有点“打开” “链式函数”的调用该调用向下移动,直到找到并解析基本情况。
\n当“基本”情况或函数得到解决时,链会“向上”移动,也就是说,解决上面的每个递归步骤,取消每个 n 值的移位,并“向上”返回结果数组,直到达到初始 n 值,此时返回最终数组,并且退出该函数。
\n我想我现在(有点)明白了!实际上编写递归函数并不容易,但我更好地掌握了这个过程的实际发生方式。
\n\n\n为什么将数组声明为常量并分配给递归调用?
\n
它不是。该数组是可变的。每次调用此函数都会返回一个新数组。没有数组被“覆盖”。const在 JavaScript 中仅用于声明一个常量变量,该常量变量具有词法作用域(即该变量arr在 后不再存在return arr)并且是常量(不能被变异/赋值)。
\n\n我得到了当前值的不变,但为什么它是在递归调用之后声明的?难道它不应该放置在之前(上面一行)以便在函数循环本身之前取消移位值吗?
\n
您不能将其放置在上面,因为arr首先需要递归调用来获取。n-1从到倒计时后1,您必须n在第一个位置添加前缀,这就是unshift执行的操作。
\n\n同样,为什么在 else 循环中返回完整的最终数组?难道不应该更好地在外部返回,或者在上面的基本条件下返回,以免每次函数循环时都返回吗?
\n
不存在“else循环”这样的东西。在那里返回数组的原因是您想countdown(n)使用 array 返回它刚刚生成的数组countdown(n-1)。
\n\n事实上,我的理解是,当 n 达到 0 时,循环应该返回一个空数组,而不是通过递归创建的完整数组。
\n
正是如此if (n < 1) return [];。
也就是说,对于手头的任务,给出的递归实现非常不理想。首先,你不需要 ifelse无论如何return都会退出该函数:
function countdown(n) {\n if (n < 1) return [];\n\n const arr = countdown(n - 1);\n arr.unshift(n);\n return arr;\n}\nRun Code Online (Sandbox Code Playgroud)\n其次,作为一个简单的循环,这是最具可读性(和性能)的for:
function countdown(n) {\n const arr = Array(n)\n for (let i = 0; i < n; i++) arr[i] = n - i;\n return arr;\n}\nRun Code Online (Sandbox Code Playgroud)\n(奖励:可以使用构造函数利用提前知道的数组大小Array,以防止 JS 在推送元素时必须调整数组大小)。
第三,当前的实现具有O(n\xc2\xb2) 的二次时间复杂度,因为arr.unshift是一个线性时间操作,将n时间应用于长度0为 的数组n-1。
当您需要堆栈时(深度优先遍历中的 fE),递归非常有用。创建倒计时不需要堆栈。这不是使用而是滥用递归来实现高度次优的算法。如果您 - 无论出于何种原因(也许您被迫使用没有循环,只有递归的纯函数式语言?) - 想要不惜for一切代价用递归调用替换完美的精细循环,只需使用尾递归和词法作用域(关闭):
function countdown(n) {\n const arr = [];\n function loop(n) {\n if (n < 1) return;\n arr.push(n);\n loop(n-1);\n }\n loop(n);\n return arr;\n}\nRun Code Online (Sandbox Code Playgroud)\n或者如果您不喜欢闭包,那么可选arr参数怎么样?这允许扭转递归,因为调用者现在拥有被arr调用者之前的号码,因此可以在后续递归调用执行相同操作之前附加其编号:
function countdown(n, arr = []) {\n if (n < 1) return arr;\n arr.push(n);\n countdown(n - 1, arr);\n return arr;\n}\nRun Code Online (Sandbox Code Playgroud)\n结果相同,但(IMO)更干净,最重要的是,代码速度明显更快。尝试运行所有这些函数n = 1e6,您会发现 freeCodeCamp 函数无法在合理的时间范围内完成,而其他函数几乎立即完成。
| 归档时间: |
|
| 查看次数: |
341 次 |
| 最近记录: |