我正在使用Eloquent Javascript,该练习要求找到一个递归解决方案,用于在此表单的嵌套列表中查找第n个元素;
var list = {
value: 1,
rest: {
value: 2,
rest: {
value: 3,
rest: null
}
}
};
在我搔了这么久之后,我正确地检查了答案。
function nth(list, n) {
if (!list)
return undefined;
else if (n == 0)
return list.value;
else
return nth(list.rest, n - 1);
我可以看到这确实产生了正确的结果,但我不明白为什么。
我对逻辑的理解如下
如果未提供列表或列表为false,则返回undefined, 否则,如果n为0,则返回列表的第一个元素 - 由list.value给出。 否则再次调用该函数,但将n递减1.
显然我必须遗漏一些东西,但我不明白这个逻辑如何能够返回第n个元素。似乎它会一直保持递归,直到n == 0。此时它将只返回列表的第一个元素。
答案 0 :(得分:3)
这是典型的功能样式链表迭代。请注意,递归调用会在list.rest
上进行,而不是再次在list
上进行。 list.rest
是以下一个元素开头的列表。在程序风格中,等同于获取列表的其余部分将是增加索引指针。
该风格的美妙之处在于您在代码中描述了列表的属性:列表的第n个元素L也是列表的第(n-1)个元素,它开始于L' s头。接近声明性表单有时可以更容易验证您所写内容的正确性。
在大型列表的情况下出现样式的缺点,其中这样的函数将进入Javascript引擎最有可能无法处理的深度递归。专用于函数式语言的编译器/解释器将在可能的情况下消除该递归,将其转换为循环,称为"尾部调用优化"的转换。 TCO将开发人员从绑定到堆栈的约束中解放出来,允许函数式语言使用递归作为迭代的主要方式,因此在这个世界中,您可以在整个地方看到和编写这样的结构。
答案 1 :(得分:0)
这句话也使我思考得很好。我终于弄清楚了,所以我觉得我应该补充上一个答案。
nth(list.rest,n-1)中的“ n-1”语句实质上是确定程序何时终止的计时器。请记住,当它变为0时,它将按照此语句返回值。
else if (n == 0)
return list.value;
例如,一个列表被传递到您的函数中 “ {值:1,休息:{值:2,休息:{值:3,休息:空}}}” 您传入2作为参数。 它调用该函数并通过if语句,然后进入递归语句。它将调用List.Rest,然后将传入的参数值减小1。 它重复以上循环,并到达递归函数,现在传递的参数是List.Rest.Rest,您的参数再次减小1并变为0。 当它通过if和else if语句时,由于参数现在为0,因此以下行被触发
else if (n == 0)
return list.value;
然后返回列表。值...。请记住,您的列表现在是list.Rest.Rest 让您现在变得有价值3。