Ramda 的转换器:懒惰的一对多分支

Izh*_*aki 5 ramda.js

Ramda 适合懒人

Ramdatransduce可以创建惰性序列

一对多

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"]
Run Code Online (Sandbox Code Playgroud)

问题

上面代码片段的问题是它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));
}));
Run Code Online (Sandbox Code Playgroud)

_makeFlat阻止了任何懒惰的评估。

目标

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

请注意, R.reduce 支持iterables

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

Sco*_*her 3

您遇到的问题是,一旦R.map(concat(suit),ranks)遇到,就会立即对其进行全面评估。这与您提到的函数无关_makeFlat,因为使用转换器时_dispatchable实际上不会调用 定义内的函数体R.chain,而是使用 内部的转换器定义_xchain

\n\n

因此,我们不是已经生成整个映射列表,而是创建一个新的转换器,我将其称为combineWith,它采用像您的示例中那样的函数concat和一个列表来组合每个元素并进行转换。我们可以一边检查一边这样做@@transducer/reduced

\n\n

\r\n
\r\n
const combineWith = (fn, xs) => xf => ({\r\n  // proxy both `init` and `result` straight through\r\n  // see internal/_xfBase.js\r\n  \'@@transducer/init\': xf[\'@@transducer/init\'].bind(xf),\r\n  \'@@transducer/result\': xf[\'@@transducer/result\'].bind(xf),\r\n\r\n  // combine the item at each step with every element from `xs`\r\n  // using `fn`, returning early if `reduced` is ever encountered\r\n  \'@@transducer/step\': (acc, item) => {\r\n    for (const x of xs) {\r\n      acc = xf[\'@@transducer/step\'](acc, fn(item, x))\r\n      if (acc[\'@@transducer/reduced\']) return acc\r\n    }\r\n    return acc\r\n  }\r\n})\r\n\r\nconst suits = [\'\xe2\x99\xa0\', \'\xe2\x99\xa5\', \'\xe2\x99\xa6\', \'\xe2\x99\xa3\']\r\nconst ranks = [\'1\', \'2\', \'3\', \'4\', \'5\', \'6\', \'7\', \'8\', \'9\', \'J\', \'Q\', \'K\', \'A\']\r\n\r\nconst tapLog = R.tap(console.log.bind(console, \'tapLog\'))\r\n\r\nconst transducer = R.compose(\r\n  combineWith(R.concat, ranks),\r\n  tapLog,\r\n  R.take(2)\r\n)\r\n\r\nconsole.log(\'result\', R.into([], transducer, suits))
Run Code Online (Sandbox Code Playgroud)\r\n
<script src="//cdnjs.cloudflare.com/ajax/libs/ramda/0.25.0/ramda.min.js"></script>
Run Code Online (Sandbox Code Playgroud)\r\n
\r\n
\r\n

\n