JavaScript精通第四章:数组转链表/链表转数组练习

4

eloquentjavascript的问题:

编写一个函数 arrayToList,当给定 [1, 2, 3] 作为参数时,它会构建出一个类似上一个数据结构的数据结构,并编写一个 listToArray 函数,该函数从列表中生成一个数组。还要编写帮助函数 prepend,它接受一个元素和一个列表,并创建一个将元素添加到输入列表前面的新列表,以及 nth 函数,它接受一个列表和一个数字,并返回列表中给定位置的元素,如果没有这样的元素,则返回 undefined。

有人能够不使用术语解释这个答案吗? 我只能按照说明学习。

这是我为 arrayToList 想出来的代码: 所以开始列表为空,创建一个 for 循环递减并在内部定义 "i" ,然后返回列表。

function arrayToList(array){ //pass list as parameter
    var list = null; // don't know why we make it null
    for (var i=array.length-1; i>=0; i--)  // why -1 on the length?
        list = {value: array[i], rest:list}; //clueless
    return list;
}

list对象内的rest:list是什么?


8个回答

4

需要修改@eloquent的最后一条评论:

var arr1 = [10, 20, 30];

function arrayToList(arr) {
  var list = {};

 for (var i = 0; i < arr.length; i++) {
    list.value = arr.splice(0, 1)[0];
    list.rest = (arr.length > 0) ? arrayToList(arr) : null;
 }

 return list;
}

console.clear();
console.log(  arrayToList(arr1) );

2
list对象内部,rest:list是什么?它创建了一个属性rest在对象文字中,并将变量list的值赋给它—在将该对象分配给变量list之前。请注意,这是在循环中完成的,因此它所做的就是获取旧值并用引用旧值的新值覆盖它。
这正是构建嵌套列表结构所需的内容。 var list = null; // don't know why we make it null 这是初始值,它将放置在列表的“末尾”。如果数组为空,则返回它,如果数组只有一个元素,则它将成为返回的对象的rest属性。如果数组有多个元素...
为什么会在长度上减1呢?
因为数组是从零开始索引的,即它们的索引从0length-1。当我们向后迭代时,我们需要这个奇怪的初始值和 i >= 0 的比较。

1

使用递归解决的方案:

function arrayToList([...arr], n = 0){
  return (n < arr.length) ? {value: arr[n], rest: arrayToList(arr, n + 1)} : null;
} 

function listToArray({...list}, arr = []){
  arr.push(list.value);
  return (list.rest != null) ? listToArray(list.rest, arr) : arr;
}

console.log(arrayToList([10, 20]));
// → {value: 10, rest: {value: 20, rest: null}}

console.log(listToArray(arrayToList([10, 20, 30])));
// → [10, 20, 30]

1
function arrayToList(arr) {
  var obj = {};
  for(var i = 0 ; i < arr.length; i++) {
    if(i == arr.length) {
     return obj.rest = null;
    }
    obj.value = arr.splice(0,1);
    obj.rest = arrayToList(arr);
  }
  return obj;
};

console.clear();
console.dir(arrayToList([1,2,3,4,5]));

0

我来自Java背景,不明白为什么在书中给出的解决方案中,最终结果不仅仅是给定参数(数组)的最后一个元素,而实际上它创建了链接列表?

list = {value: array[i], rest: list};

在我的眼中,如果你将 [1,2,3,4] 作为参数传递,那么 value 将依次等于 1、2、3 和最后是 4,不应该有任何链接,但是 rest 将指向与一开始相同的地址,只是现在 value 等于 4。 那么这是如何工作的呢?
以下是 Java 代码示例:
public class Main {

    public class MyList{
   
        public int value;
        public MyList rest;
    }
   
    public static void main(String[] args){
        Main main = new Main();
        MyList myList = main.new MyList();

        for (int i = 0; i < 5; i++) {
            myList.value = i;
            current.rest = myList;
        }
    }
}

这是输出结果:

for(int i = 0; i < 5; i++){
    System.out.println("Value: " + myList.value + " obj. ref: " + myList.rest);
    myList = myList.rest;
}

Value: 4 obj. ref: Main$MyList@1d81eb93
Value: 4 obj. ref: Main$MyList@1d81eb93
Value: 4 obj. ref: Main$MyList@1d81eb93
Value: 4 obj. ref: Main$MyList@1d81eb93
Value: 4 obj. ref: Main$MyList@1d81eb93

为了使其正常工作,我们需要每次创建新对象,因此for循环应该如下所示:
MyList current = myList;

for(int i = 0; i < 5; i++) {
    MyList newList = main.new MyList();
    current.value = i;
    current.rest = newList;
    current = newList;
}

输出结果为:

Value: 0 obj. ref: Main$MyList@1d81eb93
Value: 1 obj. ref: Main$MyList@7291c18f
Value: 2 obj. ref: Main$MyList@34a245ab
Value: 3 obj. ref: Main$MyList@7cc355be
Value: 4 obj. ref: Main$MyList@6e8cf4c6

那么在JavaScript中,list是如何重新实例化的呢?


0
不知道为什么要将其设为null。可能只是为了明确初始化值为null(而不是undefined)。无论如何,赋值null都不是必要的。
为什么长度为-1?因为长度基于1(一个包含4个项的数组的长度为4),但是对数组进行索引是基于0的(array [0]返回第一个项)。
这一步正在创建一个嵌套列表对象。第一个循环创建一个带有该数组位置上值的列表,第二个循环创建一个带有该位置上值以及指向前一个列表的指针的包装器列表,以此类推。

这里是整个问题的答案 https://gist.github.com/laichejl/32d98af04caa66bd195f 我完全困惑了,上一章我们还在学习基础知识,现在变得非常难。 - user3715090
啊,我明白了。我一开始看错了。它是将前一个列表嵌入到每个循环中的“rest”属性中。您想要解释什么? - bvaughn
1
哦,所以剩下的只是一个名字(对象的键/值对)?它可以是任何东西,对吧?为什么要等于列表? - user3715090
它只是一个随意命名的键,没错。它也可以被称为其他名称。它被用来容纳指向最后一个列表的指针,以便可以遍历最终返回的“Object”,以重构原始数组。 - bvaughn

0
一个简化的解决方案和一个更清晰的方法
function arrayToList(inputArray )  {
  if (!inputArray.length) {
    return null;
  }
  const [head, ...tail] = inputArray;
  return { value: head, rest: arrayToList(tail) };
}



0

作为免责声明,我想说我自己也是一个新手,我在这里学习东西,可能通过这样做帮助其他人。这就是我理解第二个问题已经解决的方式。

另外编写一个listToArray函数来从列表中生成一个数组。

以下代码可以使用:

function listToArray(list) {
    let arr = [];
    for(; list !== null; list = list.rest) {
      arr.push(list.value);
    }
    return arr;
}

列表参数将仅保存要插入的参数。在我们的例子中是:{value: 10, rest: {value: 20, rest: null}

请注意,Eloquent Javascript中的代码将从具有3个元素的数组[10, 20, 30]创建一个列表,但对于此示例,我们将仅使用由2个元素组成的列表。

执行时,代码将执行以下操作:

第一次(第一次循环):

  1. 省略for循环的初始化(不定义任何变量或执行任何操作)
  2. 检查列表是否不为null-此时不是,因为列表等于{value: 10, rest: {value: 20, rest: null}
  3. 使列表(函数的参数)等于list.rest
    • 请记住,.rest只是列表对象的属性,它指向另一个对象-在我们的情况下,它将指向{value: 20, rest: null}

第二次(第二个循环):

  1. 省略for循环的初始化(不定义任何变量或执行任何操作)

  2. 检查列表是否不为空-在这种情况下它不为空,因为此时列表等于{value: 20, rest: null}

  3. 将列表再次设为等于list.rest-但这次它将指向rest:null,因此此时列表的值将是rest键的值,即null

第三次(第三个循环):

  1. 省略
  2. 检查列表是否不为空-在这种情况下它为空,因此停止执行并转到返回语句
  3. 返回arr

我的答案基于:https://gist.github.com/laichejl/32d98af04caa66bd195f#file-exercise-js-L18


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