Ramda的传感器:懒惰的一对多分支

问题描述 投票:4回答:1

Ramda for the lazy

Ramda的transduce使creation of lazy sequences

One to many

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"]

The issue

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

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

Why is this happening?

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

The goal

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

请注意,R.reduce支持iterables

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

ramda.js
1个回答
3
投票

您遇到的问题是,只要遇到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>
© www.soinside.com 2019 - 2024. All rights reserved.