如何在 javascript 中克隆迭代器?

问题描述 投票:0回答:5

在 ES6 中,是否可以克隆迭代器状态?

var ma=[1,2,3,4];
var it=ma[Symbol.iterator]();
it.next();

如果我想记住这里它说明了我应该在javascritp中做什么?

里面记着什么? 自从

JSON.stringify(it) //it would just return {}
javascript ecmascript-6 iterator
5个回答
4
投票

您无法克隆任意迭代器,但您可以通过保留某种状态来创建许多不同的迭代器:

function tee(iterable) {
    const source = iterable[Symbol.iterator]();
    const buffers = [[], []];  // substitute in queue type for efficiency
    const DONE = Object.create(null);

    const next = i => {
        if (buffers[i].length !== 0) {
            return buffers[i].shift();
        }

        const x = source.next();

        if (x.done) {
            return DONE;
        }

        buffers[1 - i].push(x.value);
        return x.value;
    };

    return buffers.map(function* (_, i) {
        for (;;) {
            const x = next(i);

            if (x === DONE) {
                break;
            }

            yield x;
        }
    });
}

用途:

const [a, b] = tee(iterator);
assert(a.next().value === b.next().value);

4
投票

无法克隆迭代器。迭代器状态基本上是完全任意的,任何给定的迭代器都可能需要或产生副作用(例如,读取或写入网络流),这些副作用不可按需重复。


0
投票

这还不是正式的,但我认为在 Iterator Helpers 的第二阶段提案中可能有一个解决方案。如果这些方法不影响原始迭代器,那么执行诸如

iter.take(Infinity)
iter.drop(0)
之类的操作将具有与克隆相同的效果。


0
投票

我构建了一个库,允许您在这里分叉迭代器:https://github.com/tjenkinson/forkable-iterator

意味着你可以做类似的事情:

import { buildForkableIterator, fork } from 'forkable-iterator';

function* Source() {
  yield 1;
  yield 2;
  return 'return';
}

const forkableIterator = buildForkableIterator(Source());

console.log(forkableIterator.next()); // { value: 1, done: false }

const child1 = fork(forkableIterator);
// { value: 2, done: false }
console.log(child1.next());
// { value: 2, done: false }
console.log(forkableIterator.next());

// { value: 'return', done: true }
console.log(child1.next());
// { value: 'return', done: true }
console.log(forkableIterator.next());

如果您不再需要继续从 fork 中消费,并且提供了对它的松散引用,那么也不应该出现内存泄漏。


0
投票

您可以创建多个依赖于原始迭代器的迭代器。使用缓冲来跟踪哪些值已经被消耗以服务于这些新迭代器之一,但还不是全部。

这里是一个使用链表作为缓冲区的实现,这样所有返回的迭代器都可以使用同一个列表:

function tee(iterable, n=2) {
    const it = iterable[Symbol.iterator]();
    
    function* gen(current) {
        while (true) {
            if (!current.next) {   // when at the tail of the linked list
                const {done, value} = it.next(); // fetch a new value and
                if (done) return;
                current.next = { value }; // append it to the linked list
            }
            current = current.next; // Move up to the next node
            yield current.value;
        }
    }
    
    // All generators start at the same node {}, i.e. the empty linked list
    return Array(n).fill({}).map(gen);
}

// Example usage
const [a, b] = tee(Array(10).keys());
console.log("a", a.next().value);
console.log("b", b.next().value, b.next().value);
console.log("a", a.next().value, a.next().value, a.next().value);
console.log("b", b.next().value, b.next().value, b.next().value);
console.log("a leftover", ...a);
console.log("b leftover", ...b);

一个很好的效果是,一旦最慢的迭代器消耗了一个值,这个链表中的节点就会成为垃圾收集的候选者。

© www.soinside.com 2019 - 2024. All rights reserved.