Почему генераторы не поддерживают map()?

Мне кажется совершенно естественным, что генераторы, которые очень похожи на массивы, должны поддерживать самые основные операции с списками, такие как map(), filter() и reduce(). Я что-то пропустил?

Я написал код для map и кажется довольно простым, но было бы гораздо лучше иметь все функции, встроенные во все генераторы:

let fancyGen = g => {
  let rv = function*() {
    for (let x of g) 
      yield x;
  }
  rv.map = function*(p) {
   for (let x of g) 
      yield p(x);
  } 
  return rv;
}

Я новичок в генераторах, поэтому любые комментарии к этому коду приветствуются. В частности, лучший способ написать "генератор тождеств"?

Ответ 1

Почему генераторы не поддерживают map()?

Потому что это слишком просто для заполнения в качестве реализации пользовательской среды. В ES3 также не использовались итерационные методы Array, возможно, в ES7 появятся трансформаторы для итераторов: -)

которые очень похожи на массивы

Нет, остановите и отличите итераторы от генераторов:

  • Итератор - это объект с методом .next(), который соответствует протоколу итератора.
  • Генератор - это итератор, созданный функцией генератора (function*). Его метод .next() принимает аргумент, который является результатом каждого yield внутри функции генератора. Он также имеет методы .return() и .throw().

В основном вы будете заинтересованы в итераторах, где мы не передаем значения next и не заботимся о конечном результате - точно так же, как и в циклах for of. Мы можем легко их разложить с помощью желаемых методов:

var IteratorPrototype = Object.getPrototypeOf(Object.getPrototypeOf([][Symbol.iterator]()));
IteratorPrototype.map = function*(f) {
    for (var x of this)
        yield f(x);
};
IteratorPrototype.filter = function*(p) {
    for (var x of this)
        if (p(x))
            yield x;
};
IteratorPrototype.scan = function*(f, acc) {
    for (var x of this)
        yield acc = f(acc, x);
    return acc;
};
IteratorPrototype.reduce = function(f, acc) {
    for (var x of this)
        acc = f(acc, x);
    return acc;
};

Это должно быть достаточно для начала и наиболее распространенных случаев использования. Правильная библиотека расширит это до генераторов, чтобы значения были переданы соответствующим образом, а также будет решать проблему, что итераторы могут использоваться только один раз, прежде чем они исчерпаны (в отличие от массивов).