在JavaScript中映射生成器上的函数

dam*_*amd 11 javascript functional-programming generator

我有一个generateNumbers用JavaScript 调用的生成器和另一个生成器generateLargerNumbers,它生成每个生成的值并向其generateNumbers应用一个函数addOne,如下所示:

function addOne(value) {
  return value + 1
}

function* generateNumbers() {
  yield 1
  yield 2
  yield 3
}

function* generateLargerNumbers() {
  for (const number of generateNumbers()) {
    yield addOne(number)
  }
}
Run Code Online (Sandbox Code Playgroud)

如果没有从生成的值中构建数组,是否有任何更简单的方法可以做到这一点?我想的是:

function* generateLargerNumbers() {
  yield* generateNumbers().map(addOne) // obviously doesn't work
}
Run Code Online (Sandbox Code Playgroud)

Tha*_*you 7

高阶发生器

您可以选择自己操纵生成器功能

const Generator =
  {
    map: (f,g) => function* (...args)
      {
        for (const x of g (...args))
          yield f (x)
      },
    filter: (f,g) => function* (...args)
      {
        for (const x of g (...args))
          if (f (x))
            yield x
      }
  }

// some functions !
const square = x =>
  x * x

const isEven = x =>
  (x & 1) === 0
  
// a generator !
const range = function* (x = 0, y = 1)
  {
    while (x < y)
      yield x++
  }

// higher order generator !
for (const x of Generator.map (square, Generator.filter (isEven, range)) (0,10))
  console.log('evens squared', x)
Run Code Online (Sandbox Code Playgroud)

高阶迭代器

或者,您可以选择操纵迭代器

const Iterator =
  {
    map: (f, it) => function* ()
      {
        for (const x of it)
          yield f (x)
      } (),
    filter: (f, it) => function* ()
      {
        for (const x of it)
          if (f (x))
            yield x
      } ()
  }

// some functions !
const square = x =>
  x * x
  
const isEven = x =>
  (x & 1) === 0

// a generator !
const range = function* (x = 0, y = 1)
  {
    while (x < y)
      yield x++
  }
  
// higher-order iterators !
for (const x of Iterator.map (square, Iterator.filter (isEven, range (0, 10))))
  console.log('evens squared', x)
Run Code Online (Sandbox Code Playgroud)

建议

在大多数情况下,我认为操作迭代器更为实用,因为它具有定义良好的接口(尽管很笨拙)。它允许您做类似的事情

Iterator.map (square, Iterator.filter (isEven, [10,11,12,13]))
Run Code Online (Sandbox Code Playgroud)

而另一种方法是

Generator.map (square, Generator.filter (isEven, Array.from)) ([10,11,12,13])
Run Code Online (Sandbox Code Playgroud)

两者都有用例,但我发现前者比后者好得多


持久迭代器

JavaScript的有状态迭代器使我很烦恼–随后的每次调用都将.next不可逆地更改内部状态。

但!没有什么可以阻止您创建自己的迭代器,然后创建适配器以插入JavaScript的堆栈安全生成器机制

如果您对此感兴趣,那么您可能会喜欢在这里找到的一些其他附带示例:循环到我的对象中的文件系统结构以获取所有文件

唯一的收获不是我们可以重用持久性迭代器,而是通过这种实现,由于具有备忘录功能,后续读取甚至比第一次读取还要 –得分:JavaScript 0,持久性迭代器2

Iterator.map (square, Iterator.filter (isEven, [10,11,12,13]))
Run Code Online (Sandbox Code Playgroud)

  • 您的“高阶”迭代器和持久迭代器(顺便说一句非常好)都表明了命令式迭代器概念的缺点。迭代器不是函数,因此您必须使用函数包装器来使其可组合。但是迭代器也不是数据类型,因此您必须记住 getter 计算才能重用它。因此,迭代器是一个有状态的 getter 对象,与其数据源紧密耦合。或者正如我所说:这是迭代算法最糟糕的起点。换能器/短路是更好的选择。 (2认同)

4ca*_*tle 6

没有内置的映射Generator对象的方法,但是您可以滚动自己的函数:

const Generator = Object.getPrototypeOf(function* () {});
Generator.prototype.map = function* (mapper, thisArg) {
  for (const val of this) {
    yield mapper.call(thisArg, val);
  }
};
Run Code Online (Sandbox Code Playgroud)

现在您可以执行以下操作:

function generateLargerNumbers() {
  return generateNumbers().map(addOne);
}
Run Code Online (Sandbox Code Playgroud)

const Generator = Object.getPrototypeOf(function* () {});
Generator.prototype.map = function* (mapper, thisArg) {
  for (const val of this) {
    yield mapper.call(thisArg, val);
  }
};
Run Code Online (Sandbox Code Playgroud)