JavaScript / ES6: как вернуть несколько итераторов из класса? - PullRequest
1 голос
/ 10 апреля 2020

Я реализую двусвязный список как часть упражнения по программированию, и я хотел бы позволить разработчику перебирать его узлы как вперед, так и назад, используя нотацию for...in.

На своем По большей части c, структура данных выглядит следующим образом:

class DoublyLinkedList {
    constructor(data) {
        if (data) {
            this.head = new DoublyLinkedListNode(data)
        } else {
            this.head = null
        }
    }

    append = (data) => {
        if (!this.head) {
            this.prepend(data)
        } else {
            const newTail = new DoublyLinkedListNode(data)
            let current = this.head
            while(current.next) {
                current = current.next
            }

            current.next = newTail
            newTail.prev = current
        }
    }
}

Затем я добавил функции генератора:

    *values() {
        let current = this.head
        while (current) {
            yield current.data;
            current = current.next;
        }
    }

    *valuesBackward() {
        let currentForwards = this.head
        while (currentForwards.next) {
            currentForwards = currentForwards.next
        }

        const tail = currentForwards
        let currentBackwards = tail
        while (currentBackwards) {
            yield currentBackwards.data
            currentBackwards = currentBackwards.prev
        }
    }

Я могу добавить один итератор пересылки с в класс добавлено следующее:

[Symbol.iterator]() { return this.values()}

Я попытался добавить оба класса в класс:

iterateForward = () => [Symbol.iterator] = () => this.valuesBackward()
iterateBackward = () => [Symbol.iterator] = () => this.valuesBackward()

А затем попытался выполнить итерацию, используя for (node in list.iterateForward()), но это не удалось с ошибкой TypeError: undefined is not a function.

Полагаю, это имело смысл, глядя на код, поэтому в следующий раз я попытался:

    iterateForward = () => {
        const vals = this.values()

        const it = {
            [Symbol.iterator]() {
                return vals()
            }
        }
        return it
    }

Это не ошибка, но итерация не сработала - итератор выполнил ноль раз.

Что мне здесь не хватает? Можно ли добиться того, чего я хочу?

1 Ответ

1 голос
/ 10 апреля 2020

Этот материал меня постоянно смущает, поэтому вот краткое изложение, к которому мы оба можем обратиться.

Вот фон

iterable - это объект, имеющий * Свойство 1008 *, а затем вы, когда вы вызываете это свойство как функцию, возвращает объект итератора.

Объект итератора имеет свойство .next(), и каждый раз, когда вы вызываете эту функцию, он возвращает объект с ожидаемые свойства {value: x, done: false}. Объект итератора обычно сохраняет состояние итерации в этом отдельном объекте (таким образом, итераторы могут быть независимы друг от друга).

Таким образом, чтобы поддерживать несколько итераторов, вы создаете несколько методов, где каждый метод возвращает объект, который является разные итерируемые, каждый имеет свой собственный [Symbol.iterator], который при вызове возвращает другой объект итератора.

Итак, подведем итог:

  1. Вызовите метод, который возвращает итерируемый
  2. Итерируемый - это объект, имеющий свойство [Symbol.iterator] и доступ к данным исходного объекта.
  3. Когда вы вызываете функцию в этом свойстве [Symbol.iterator], вы получаете итератор object.
  4. Объект итератора содержит метод .next(), который получает каждый элемент в последовательности, и делает это, возвращая объект, подобный этому {value: x, done: false} каждый раз, когда вы вызываете .next().

Вы можете пропустить шаг 1, и у вашего основного объекта будет свойство [Symbol.iterator]. Это по сути становится вашей итерацией по умолчанию. Если вы сделаете:

for (let x of myObj) {
    console.log(x);
}

, он получит доступ к myObj[Symbol.iterator](), чтобы получить итератор. Но если вы хотите иметь более одного способа итерации вашей коллекции, то вы создаете отдельные функции, каждая из которых возвращает свою собственную итерацию (свой собственный объект со своим собственным свойством [Symbol.iterator]).


В массиве у вас есть .entries() и .values() как пример двух методов, которые возвращают разные итерации, которые делают разные итераторы.

let x = ['a', 'b', 'c'];

for (let v of x.values()) {
    console.log(v);                
}

Это дает вывод:

'a'
'b'
'c'

Или, для .entries():

let x = ['a', 'b', 'c'];

for (let v of x.entries()) {
    console.log(v);                
}

[0, "a"]
[1, "b"]
[2, "c"]

Итак, каждый из .values() и .entries() возвращает разные объекты, каждый из которых имеет различный [Symbol.iterator], который при вызове в качестве функции возвращает другая функция итератора для их уникальной последовательности.

И, в случае массива, .values() возвращает функцию, которая при вызове дает вам точно такой же итератор, что и просто итерация массива напрямую (например, * 1062) * свойство самого массива).

Теперь для вашей конкретной c ситуации

Вы хотите создать два метода, скажем, .forward() и .backward() что каждый создает объект остроумие ха [Symbol.iterator] свойство, которое является функцией, которая при вызове возвращает свой уникальный объект итератора.

Таким образом, obj.forward() вернет объект со свойством [Symbol.iterator], которое является функцией, которая при вызове возвращает объект итератора с соответствующим свойством .next() для итерации вперед и соответствующим начальным состоянием.

Итак, obj.backward() вернет объект со свойством [Symbol.iterator], которое является функцией, которая при вызове возвращает объект итератора с соответствующее .next() свойство для итерации в обратном направлении и соответствующее начальное состояние.

Вот пример использования массива:

class myArray extends Array {
    forward() {
        return this;    // core object already has appropriate forward
                        // [Symbol.iterator] property, so we can use it
    }
    backward() {
        return {
            [Symbol.iterator]: () => {
                let i = this.length - 1;      // maintains state in closure
                return {
                    next: () => {             // get next item in iteration
                        if (i < 0) {
                            return {done: true};
                        } else {
                            return {value: this[i--], done: false};
                        }
                    }
                }
            }
        }
        
    }
}


let x = new myArray('a', 'b', 'c');

console.log("Forward using default iterator")
for (let v of x) {
    console.log(v);
}

console.log("\nUsing .forward()")
for (let v of x.forward()) {
    console.log(v);
}

console.log("\nUsing .backward()")
for (let v of x.backward()) {
    console.log(v);
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...