ramda.js - Ramda 的传感器 : Lazy one-to-many branch

标签 ramda.js

Ramda 适合懒人

Ramda 的 transduce启用 creation of lazy sequences .

一对多
R.chain可以在转换器中用作一对多运算符,如下所示 ( REPL ):

const tapLog = R.tap( (what) => console.log(what) )

const suits = ['♠', '♥', '♦', '♣']
const ranks = ['1', '2', '3', '4', '5', '6', '7', '8', '9', 'J', 'Q', 'K', 'A']

const addRank = (suit) => R.map(concat(suit),ranks)

var transducer = R.compose(
R.chain(addRank),
tapLog,
R.take(2)
);

R.into([], transducer, suits);

// => ♠1 // console.log
// => ♠2 // console.log
// => ["♠1", "♠2"]

问题

上面代码片段的问题在于 R.map(concat(suit),ranks)不会懒惰 - 所有的等级都将被映射(创建中间阵列),只有这样链才会将它们“管道”到传感器序列中。

这不是问题,除非您要映射 680k 图形节点。

为什么会这样?
R.chain的实现看起来像这样:
var chain = _curry2(_dispatchable(['fantasy-land/chain', 'chain'], _xchain, function chain(fn, monad) {
if (typeof monad === 'function') {
    return function(x) { return fn(monad(x))(x); };
}
return _makeFlat(false)(map(fn, monad));
}));

它是 _makeFlat这会阻止任何懒惰的评估。

目标

有没有办法创建一个懒惰的一对多转换器分支?

请注意,R.reduce 支持 iterables .

另请参阅 related github issue ,其中提供了解决方案,但不使用 ramda - 这就是我所追求的。

最佳答案

您遇到的问题是,尽快R.map(concat(suit),ranks)遇到,它会立即得到完整的评估。这与_makeFlat无关您提到的功能,如使用换能器时 _dispatchable实际上不会调用 R.chain 定义中的函数体而是使用 _xchain 中的换能器定义.

因此,与其生成一个完整的映射列表,不如创建一个新的传感器,我将其命名为 combineWith。 ,它需要一个类似 concat 的函数在您的示例和列表中,将每个元素与经过转换相结合。我们可以在检查 @@transducer/reduced 时这样做一路上。

const combineWith = (fn, xs) => xf => ({
  // proxy both `init` and `result` straight through
  // see internal/_xfBase.js
  '@@transducer/init': xf['@@transducer/init'].bind(xf),
  '@@transducer/result': xf['@@transducer/result'].bind(xf),

  // combine the item at each step with every element from `xs`
  // using `fn`, returning early if `reduced` is ever encountered
  '@@transducer/step': (acc, item) => {
    for (const x of xs) {
      acc = xf['@@transducer/step'](acc, fn(item, x))
      if (acc['@@transducer/reduced']) return acc
    }
    return acc
  }
})

const suits = ['♠', '♥', '♦', '♣']
const ranks = ['1', '2', '3', '4', '5', '6', '7', '8', '9', 'J', 'Q', 'K', 'A']

const tapLog = R.tap(console.log.bind(console, 'tapLog'))

const transducer = R.compose(
  combineWith(R.concat, ranks),
  tapLog,
  R.take(2)
)

console.log('result', R.into([], transducer, suits))
<script src="//cdnjs.cloudflare.com/ajax/libs/ramda/0.25.0/ramda.min.js"></script>

关于ramda.js - Ramda 的传感器 : Lazy one-to-many branch,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/47763857/

相关文章:

javascript - Ramda 可以有 isString 吗?

javascript - 在这种情况下如何作曲

javascript - 将函数列表应用于 Javascript 中的可能值

functional-programming - 无积分编程拼图

javascript - Ramda.js - propEq(String → a → Object → Boolean) - 这个符号是什么意思?

javascript - 将具有副作用的简单算法重构为 FP 风格 - 更新任务状态

javascript - 更改对象的嵌套属性

javascript - 如何使用 Ramda.js 映射对象值并仅获取第一个元素?

javascript - 使用 Ramda 对 js 中的 JSON 对象进行通用过滤

javascript - 使 Ramda.js 函数全局可访问(无需 R.)