【问题标题】:This question is about an exercise in the book Eloquent JavaScript这个问题是关于 Eloquent JavaScript 一书中的一个练习
【发布时间】:2021-07-15 04:15:24
【问题描述】:

本练习的最后一部分是编写一个递归函数,它接受两个参数,分别是连接列表和索引。该函数将在列表中的相应索引处查找对象中的值。我编写的代码按我想要的方式工作(我可以看到它在每次调用该函数时都在 console.log 中工作。但在最后一次它引用 undefined 作为我的值。我不明白为什么。哦,它有效索引为 0。代码如下。

首先,列表如下所示:

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



   const nth = (list, targetNum) => {
   let value = Object.values(list)[0];
   if (targetNum == 0) {
       return value;
   } else {
       targetNum = targetNum -1;
       list = Object.values(list)[1];
    //    console.log(value);
    //    console.log(targetNum);
    //    console.log(list);
       nth(list, targetNum);
   }
};
console.log(nth(arrayToList([1,2,3]),2)); 

下面是 arrayToList 的代码,它是练习的第一部分,如果你有任何很酷的 cmets,导致提示最终建议从最后构建列表。

const arrayToList = (arr) => { 
    let list = {
        value: arr[0],
        rest: nestObject()
    };
    function nestObject() {
        let rest = {};
        arr.shift();
        const length = arr.length;
        if (length == 1) {
            rest.value = arr[0];
            rest.rest = null;
        } else {
            rest.value = arr[0];
            rest.rest = nestObject();
        }
        return rest;   
    }
    return list;
};

【问题讨论】:

    标签: javascript arrays list object


    【解决方案1】:

    你只需要在递归调用nth时添加一个return。否则执行逻辑但不返回值(除非targetNum为0)

    const nth = (list, targetNum) => {
       let value = Object.values(list)[0];
       if (targetNum == 0) {
           return value;
       } else {
           targetNum = targetNum -1;
           list = Object.values(list)[1];
           return nth(list, targetNum); // return needed here too
       }
    };
    

    或者更简洁:

    const nth = (list, n) => n === 0 ? list.value : nth(list.rest, n - 1)
    

    这是另一个非递归的arrayToList,它从末尾构建列表:

    const arrayToList = arr => arr.slice().reverse().reduce((rest, value) => ({value, rest}), null);
    

    (这里的slice只是为了复制数组,以免原地反转。)

    【讨论】:

    • 哦,我明白了,调用函数是不够的,您实际上必须将函数调用作为值返回。谢谢。
    【解决方案2】:

    这两种解决方案都令人费解且不必要的冗长。实际上,这两个函数都可以是单行的。这里有一些提示:

    对于toList 的事情,请考虑以下几点:

    • 如果输入数组为空,则返回null(基本情况)

    • 否则,将输入数组拆分为“head”(=第一个元素)和“tail”(=其余元素)。例如[1,2,3,4] => 1 and [2,3,4]

    • 返回一个对象,value 等于“头”,rest 等于 toList 应用于“尾”(递归)

    在更高级的说明中,拆分可以直接在函数签名中进行解构:

    const toList = ([head=null, ...tail]) => ...
    

    nth(list, N) 也是如此

    • 如果N 为零,则返回list.value(基本情况)

    • 否则,返回带有参数list.restN-1(递归)的nth 应用程序

    同样,签名可以从解构中受益:

    const nth = ({value, rest}, n) => 
    

    完整代码,如果你有兴趣:

    const toList = ([value = null, ...rest]) =>
        value === null
            ? null
            : {value, rest: toList(rest)}
    
    const nth = ({value, rest}, n) =>
        n === 0
            ? value
            : nth(rest, n - 1)
    
    //
    
    let lst = toList(['a', 'b', 'c', 'd', 'e', 'f'])
    // or simply toList('abcdef')
    
    console.log(lst)
    
    console.log(nth(lst, 0))
    console.log(nth(lst, 4))

    【讨论】:

    • 是的,非常好。我写了一个带有解构的版本,你可能会说它仍然很冗长,因为它还不是一个单行。第n个是类似的逻辑。谢谢。
    • @AnthonyT:我不必按字面意思排在一行,单行我们通常的意思是“一个箭头函数,它是一个简短的表达式”。如果您有兴趣,我会在隐藏的 sn-p 中分享我的变体。
    • 我明白了。再次感谢你。是的,这是我在阅读您的提示后写的,只是语法不同。我将研究如何使用您在 sn-p 中编写的内容。似乎也是其他人使用的。
    【解决方案3】:

    Georg’s recursive solutions 很漂亮!

    我想添加来自the book 的提示“从头开始构建列表”解决方案:

    const arrayToList => (arr) => {
      var list
      while (arr.length) {
        list = {value: arr.pop(), rest: list}
      }
      return list
    }
    

    【讨论】:

    • @AnthonyT 书中没有完整的解决方案。它只给出了list = { value: X, rest: list } 的单行提示。我将其扩展为完整的解决方案。
    • 这看起来与书中的解决方案有点不同。我仍在试图弄清楚你的如何向后记录所有内容,以及如何操纵你的休息:null,而不是在最深的嵌套对象中未定义。
    • 是的,在手机上实际上有一个部分提供完整的解决方案:)
    • 我使用 arr.pop() 从数组末尾返回元素,因此向后。要拥有rest: null,只需初始化var list = null 即可。
    • 哦,我知道它现在是如何倒退的了,非常酷,谢谢。
    猜你喜欢
    • 2021-07-22
    • 2022-11-18
    • 1970-01-01
    • 2022-12-13
    • 1970-01-01
    • 2022-11-21
    • 1970-01-01
    • 2020-03-19
    • 2017-01-02
    相关资源
    最近更新 更多