【问题标题】:Make a linked list instance accessible by index and iterable使链表实例可通过索引和可迭代访问
【发布时间】:2021-07-18 04:06:16
【问题描述】:

我正在开发一个库,它将为链表实现数组函数。

我想使用linkedlistobject[i] 按索引获取一个值,并使用linkedlistobject[i] = x 按索引更改一个值。

如何使我的类可迭代?例如,在 Python 中,可以使用一些魔术函数使其可迭代(如果我没记错的话,它是 __iter__)。 如何创建可与 [ ] 表示法一起使用的可迭代对象?

【问题讨论】:

  • 它是如何完成的在the documentation中有完整的描述。
  • obj[index] 表示法和迭代(for…of)是两个完全不同的东西——在 JS 中就像在 Python 中一样。你要哪个?
  • 我想要linkedlistobject[index] 表示法。

标签: javascript arrays oop linked-list iterator


【解决方案1】:

能够通过索引访问值与使对象可迭代不同。在 Python 中也是如此:实现 __iter__ 不会提供按索引访问值的可能性。

所以这里有两个不同的概念。我们可以通过实现Symbol.iterator 生成器方法使对象可迭代。我们可以通过使用Proxy 设置属性访问陷阱来提供索引访问。

下面的demo提供了一个链表,方法如下:

  • _nodeAt: 将获取指定索引处的节点。当代理检测到被访问的属性表示一个整数(索引)时,将调用此方法。与 Array 不同,负整数也将被解释为索引,从列表的后面开始计数。

  • Symbol.iterator: 允许迭代列表的值。

  • push: 像数组一样,可以接受可变数量的值,这些值将被添加到列表中。当给定一个或多个值来初始化链表时,构造函数会调用此方法。与 Array 构造函数相反,只提供一个值并没有特殊的长度含义。

  • length: 就像数组一样,这是一个 getter/setter。但是,它不允许将列表扩展到比它已经拥有的更长的长度。

  • toString:由箭头字符分隔的字符串表示形式。

一个实例只维护一个对头节点的引用。它不跟踪当前长度也不跟踪尾节点。这可能是以后添加的一个选项,但也会增加开销。

演示说明了如何构造列表、如何通过索引访问值、如何通过索引修改以及可以迭代:

class Node {
    constructor(value, next=null) {
        this.value = value;
        this.next = next;
    }
}

class LinkedList {
    constructor(...values) {
        this._head = null;
        this.push(...values);
        return new Proxy(this, {
            get(that, prop) {
                return typeof prop === "string" && /^(0|-?[1-9]\d*)$/.test(prop) 
                    ? that._nodeAt(+prop).value 
                    : Reflect.get(...arguments);
            },
            set(that, prop, value) {
                return typeof prop === "string" && /^(0|-?[1-9]\d*)$/.test(prop)
                    ? (that._nodeAt(+prop).value = value) 
                    : Reflect.set(...arguments);
            }
        });
    }
    _nodeAt(i) {
        // Contrary to Array, support negative indexes: they count from the back of the list
        if (i < 0 && (i += this.length) < 0) throw new Error("Invalid index"); 
        let node = this._head;
        while (i-- > 0 && node) node = node.next;
        if (!node) throw new Error("Invalid index");
        return node;
    }
    push(...values) {
        if (!values.length) return;
        if (!this._head) this._head = new Node(values.shift());
        let tail = this._nodeAt(-1);
        for (let value of values) tail = tail.next = new Node(value);
        // Contrary to Array, return the linked list, not its new length
        return this; 
    }
    get length() {
        let count = 0;
        for (let _ of this) count++;
        return count;
    }
    set length(length) {
        if (length == 0) {
            this._head = null;
        } else try {
            this._nodeAt(length - 1).next = null;
        } catch {
            throw new Error("Invalid length");
        }
    }
    * [Symbol.iterator]() {
        for (let node = this._head; node; node = node.next) yield node.value;
    }
    toString() {
        return "«" + [...this].join("→") + "»";
    }
}

// Create list: a->b->c->d
let list = new LinkedList("a", "b", "c", "d");
console.log("initial list to string: " + list);

console.log("index access:");
for (let i = 0; i < list.length; i++) {
    console.log(list[i]);
}
console.log("last is: " + list[-1]);
console.log("double each value");
for (let i = 0; i < list.length; i++) {
    list[i] += list[i];
}
console.log("" + list);
console.log("iterate:");
for (let value of list) {
    console.log(value);
}
console.log("convert to array:");
console.log([...list]);
console.log("set length to 2:");
list.length = 2;
console.log("" + list);

【讨论】:

    猜你喜欢
    • 2011-07-18
    • 2018-05-07
    • 2010-09-22
    • 2021-05-02
    • 2018-11-01
    • 1970-01-01
    • 1970-01-01
    • 2016-06-15
    • 1970-01-01
    相关资源
    最近更新 更多