Почему эта оболочка функции генератора потребляет итератор, когда я создаю новый генератор при каждом вызове функции? - PullRequest
0 голосов
/ 05 февраля 2020

При прямом уменьшении только что созданной Последовательности я могу уменьшить несколько раз один и тот же seq, но если я пытаюсь многократно уменьшить отключение Последовательности, созданной из .map, это работает только в первый раз?

Кажется вероятным, что я потребляю оригинальный итератор ... но я не вижу, как / где. Насколько я понимаю этот код, Sequence :: map создает новый генератор для итерации при каждом его вызове, поэтому он должен иметь возможность повторно использовать один и тот же seq каждый раз. Может кто-нибудь помочь мне понять, чего мне не хватает?

// A wrapper for a generatorFn
class Sequence {

  constructor(generatorFn) {
    if (typeof generatorFn === "function") {
      this[Symbol.iterator] = generatorFn;
    } else {
      throw new Error("Cannot create Sequence from provided argument");
    }
  }


  static of(obj) {
    //Create a generatorFn from an iterable object  
    function* genFn() {
        for (const x of obj) {
        yield x;
      }
    }
    return new Sequence(genFn);
  }

  map(fn) {
    //Make a generator from the generator function
    const gen = this[Symbol.iterator]();

    //Make a new generator function that applies fn to each value
    function* mapGenFn() {
      for (const x of gen) {
        yield fn(x);
      }
    }

    // Create a new sequence using the new generator function
    return new Sequence(mapGenFn);
  }

    reduce(fn, initial) {
    let acc;
    let first = true;
    // Make a new generator from the seq's generator fn
    const gen = this[Symbol.iterator]();
    if (initial) {
      acc = initial
      // iterate on the new generator
      for (const x of gen) {
        acc = fn(acc, x);
      }
    } else {
        acc = this.fold(fn);
    }
    return acc;
  }

  fold(fn) {
    const gen = this[Symbol.iterator]();
    let first = true;
    let acc;
    for (const x of gen) {
        if (first) {
            acc = x;
          first = false;
        } else {
          acc = fn(acc, x);
        }
    }
    return acc;
  }
}

const seqA = Sequence.of([1,2,3])
console.log("SeqA1 is: ", seqA.reduce((acc, x) => acc + x)); // 6
console.log("SeqA2 is: ", seqA.reduce((acc, x) => acc + x)); // 6
console.log("SeqA3 is: ", seqA.reduce((acc, x) => acc + x)); // 6

const seqB = Sequence.of([1,2,3]).map(x => x + 1);
console.log("SeqB1 is: ", seqB.reduce((acc, x) => acc + x)); // 9
console.log("SeqB2 is: ", seqB.reduce((acc, x) => acc + x)); // undefined
console.log("SeqB3 is: ", seqB.reduce((acc, x) => acc + x)); // undefined

Скрипка, демонстрирующая проблему: https://jsfiddle.net/qsh9mupz/5/

1 Ответ

1 голос
/ 05 февраля 2020

const gen = this[Symbol.iterator]() - это , а не , создаваемое каждый раз, когда вы используете новую последовательность, но только один раз, когда вы вызываете .map(…). Переместите эту часть внутрь mapGenFn:

map(fn) {
  const gen = this[Symbol.iterator]; // no call here!
  return new Sequence(function* mapGenFn() {
    for (const x of gen()) {
//                     ^^ create new iterator here every time the sequence uses mapGenFn
      yield fn(x);
    }
  });
}
...