【问题标题】:Why do generators not support map()?为什么生成器不支持 map()?
【发布时间】:2015-09-22 19:22:04
【问题描述】:

在我看来,功能非常类似于数组的生成器应该支持非常基本的列表操作,如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;
}

我是生成器的新手,因此欢迎使用代码中的任何 cmets。特别是,这是编写“身份生成器”的最佳方式吗?

【问题讨论】:

    标签: iterator generator ecmascript-6 map-function


    【解决方案1】:

    为什么生成器不支持 map()?

    因为填写为用户态实现太容易了。 ES3 也不包含数组迭代方法,也许会在 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;
    };
    

    这些对于开始和最常见的用例应该足够了。适当的库会将其扩展到生成器,以便适​​当地传递值,并且还将处理迭代器在耗尽之前只能使用一次的问题(与数组相反)。

    【讨论】:

    • 区分生成器和迭代器就像区分贵宾犬和狗一样——贵宾犬狗,尽管并非所有狗都是贵宾犬。与犬类的情况相反,不是生成器的迭代器很古怪且难以使用,因此我将我的兴趣限制在生成器上(以及通过for..of 语法访问)。
    • 在我看来reduce() 不应该是生成器函数。
    • @Malvolio:事实上,生成器比日常迭代器更加古怪和难以使用。您很少遇到它们 - 所有[Symbol.iterator] 方法都返回迭代器,而不是生成器。通过for of 访问生成器不会使用它们的全部功能。