这个用于创建范围的递归函数如何工作

How can this recursive function for creating a range work?

本文关键字:何工作 工作 递归函数 用于 创建 范围      更新时间:2023-09-26

从这个SO问题的选定答案中,这个非常巧妙的函数创建了一个范围从1到i的数组:

function range1(i){return i?range1(i-1).concat(i):[]}

它工作完美。说我愚蠢,但我就是无法理解它是如何工作的。假设我们有range1(5).现在进入函数,我们有 i ,所以它返回参数 i-1 (4( 并将i (5( 连接到它。但在这里我陷入了困境:range1怎么知道它与数组有关?我会说在第一次运行后返回值(只要我们有i,所以i!==0(将是一个数字。数字没有concat方法。有人可以解释一下吗?我错过了什么?

我扩展了这段代码,因为我发现这样更容易理解。

function range1(i){
  if (i != 0) {
     return range1(i - 1).concat(i);
  } else {
     return [];
}

这个函数背后的逻辑是,如果你想要3元素的列表(range(3)(,你取2元素的列表(range1(i - 1)(,并在它的末尾添加3 .concat(i)。除此之外,您只需要处理特殊情况,即range1(0)是空数组[]即可完成。

想象一下给range1(2)打电话.自i != 0年以来,我们得到

range(2) = range(1).concat(2)

range(1)返回range(0).concat(1),给我们

range(2) = range(0).concat(1).concat(2)

那么,什么是range(0)?自i == 0以来,我们得到了我们需要的空数组([](!

range(2) = [].concat(1).concat(2) -> [1, 2]

现在进入函数,我们有 i, 所以它返回带有参数的自身 I-1 (4( 并连接 I (5(。

不,它不会自行返回。它所做的是调用自身,即递归,然后返回该调用的结果,最后一个元素连接起来。

所以,range1(5)会调用range1(4),这会调用range1(3),依此类推。当它达到零时,它将停止调用并返回一个空数组。

range1(0)返回[],所以range1(1)返回[].concat(1) [1],然后range1(2)返回[1].concat(2) [1,2],依此类推。当我们回到range1(5)它返回[1,2,3,4].concat(5)这是[1,2,3,4,5] .

注意:此函数非常适合创建小数组,但是如果您需要一个大数组,则创建数组并使用常规循环填充它会快得多。

递归的基本情况是 [] ,因此递归的尾部将返回一个数组,其他步骤将连接到[](和前面的步骤(。

让我们以范围 1(4( 为例:

range1(4) => range1(3).concat(4)
range1(3) => range1(2).concat(3)
range1(2) => range1(1).concat(2)
range1(1) => range1(0).concat(1)
range1(0) => []

现在取第一行,并将 range1(3( 替换为下一行的等效项。你得到这个:

range1(4) => range1(2).concat(3).concat(4)

继续替换 range1 引用,直到没有剩余引用。最终结果:

range1(4) => [].concat(1).concat(2).concat(3).concat(4)

range1 函数始终返回一个数组。

它要么是一个空数组(对于 i == 0(,要么是一个串联数组。

在基本情况下,range1返回一个定义concat的空数组。当它展开时,范围内的数字被添加到数组中。

直到我变成 0 除了使用重定向值调用函数之外,什么都不会发生,但是当它用 0 调用时,它会返回 [] - 上一次调用中的一个空数组应用了一个 concat 方法......看起来 range(0(.concat(1( => [].concat(1( 所以 [1] 返回到上一个调用: [1].concat(2( 等等开始