列表转数组函数的递归版本

4

我刚接触JavaScript。目前我正在阅读Marijn Haverbeke的《JavaScript高级程序设计》并尝试编写一个能够从列表生成数组的函数。使用循环是一种相当简单的方法:

function listToArray(list) {
  let array = [];
  for (let node = list; node; node = node.rest) {
    array.push(node.value);
  }
  return array;
}

然而,我想以递归的方式来编写这个程序。我尝试把它放在这里:

列表:

{
  value: 1,
  rest: {
    value: 2,
    rest: {
      value: 3,
      rest: null
    }
  }
}

功能:

function recursiveNth(list) {
  let array = [];
  if (list.rest == null) {
    array.push(list.value);
    return array;
  } else {
    array.push(list.value);
    return recursiveNth(list.rest);
  }
}

输出:

[3]

1
你可以将 array 作为参数传递给 recursiveNth。否则,每次调用都会丢失状态。 - fahrradflucht
2个回答

3
你的问题是每次递归时都会创建一个新数组,然后将其推入该新数组中,然而,你从未在进一步的递归调用中向这些数组添加内容。因此,一旦你最终达到基本情况并且return array,你会将[3]返回给函数的调用者,该调用者是recursiveNth(list.rest);return recursiveNth(list.rest);中。然后这会评估为return [3]。这个循环会一直持续到recursiveNth();方法的原始调用者,从而产生[3]作为你的结果。
相反,你可以返回一个单独的数组,并通过使用进一步的后续调用recursiveNth()来使用.concat()添加到该数组,如下所示:

const list = { value: 1, rest: { value: 2, rest: { value: 3, rest: null } } };

function recursiveNth(list) {
  if (list.rest == null) {
    return [list.value];
  } else {
    return [list.value].concat(recursiveNth(list.rest));
  }
}

console.log(recursiveNth(list));


1
是的,你说得对。我的元素每次都被推到新数组中。现在我能看到我的错误了。我真的很喜欢你的答案,它简单明了。谢谢。 - Karolina Hudziec
这里的基本情况可能应该使用return [list.value],将元素包装在数组中,以便单个元素的列表返回数组,多个元素的列表也是如此。 - Scott Sauyet

3

您可以返回值加上递归调用的结果,也可以返回一个空数组。

主要函数的作用是检查list是否具有truthy值,如对象或可转换为布尔值的任何值都为true,并在这种情况下返回value属性及带有rest属性的展开(展开语法...)递归调用的结果数组。

如果您得到一个falsy值,即与truthy相反的值,例如undefinednull,那么只返回一个空数组。这是在检查退出条件后的返回值。

要使递归函数的各个部分可视化,您需要以下部分(抽象伪代码):

function r(value)
    if (exitCondition) return someValue;         // or an empty data structure
    return value + result of call of r(newValue) // this result can be another data structure
}

对于下面的方法,退出条件被否定了,如果没有找到退出值,则使用一个值和递归结果进入。

function recursive(list) {
    return list
        ? [list.value, ...recursive(list.rest)]
        : [];
}

var list = { value: 1, rest: { value: 2, rest: { value: 3, rest: null } } };

console.log(recursive(list));


"我在Javascript方面还很新。" - 这是一个带有ES6特性的答案,但没有进一步的解释或好的文档链接,或者至少没有提到该特性的名称... - Andreas
1
@Andreas:为什么对于新手来说,这种情况比以前的功能更真实呢?我现在先教箭头函数然后从一开始就使用rest/spread运算符。 - Scott Sauyet
@ScottSauyet 因为《JavaScript 高级程序设计》和大部分初学者教程都没有讨论 ES6/7 或任何更新的内容(tc39)。 - Andreas
1
@Andreas: 《JavaScript高级程序设计》当前版本经常使用箭头函数,引入了async-await,介绍了rest和spread,解释了模板字符串,但据我回忆,并没有试图区分ES6功能和旧功能。当然,任意教程都是混合的,但有很多使用ES6的例子。 - Scott Sauyet
总体来说,这是一种非常有趣的方法。使用三元运算符不是我自己能够想出来的(至少目前还没有)。我可以理解语法,但仍然存在一些问题。在最后一个情况中,当函数达到其峰值并且列表为false(因为list.rest为空),函数只返回“[ ]”。所以如果这是最后一步,为什么函数最终不返回“[ ]”,而是返回整个数组呢? - Karolina Hudziec
显示剩余2条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接