ramda 使って transduce のお勉強
- まずは 出力用の reducer の定義。ふつうに
(acc, x) => acc.concat([x])
としてもいいんだけど、せっかくだから transformer で実装する:
const R = require('ramda');
const xfList = {
'@@transducer/step': (acc, x) => acc.concat([x]),
'@@transducer/init': () => [], // ignored on transduce
'@@transducer/result': x => x,
};
- 2を足して 3を掛けるという map を transduce で書いてみる。mapper を関数合成するのが普通:
// reference point
const add2 = x => x + 2;
const mul3 = x => x * 3;
const f1 = x => mul3(add2(x));
[1, 2, 3].map(f1); // -> [ 9, 12, 15 ]
R.transduce(R.map(f1), xfList, [], [1, 2, 3]); // -> [ 9, 12, 15 ]
- mapper を compose しても良いし、 transducer を compose してもよい。compose の順序が異なるのは何度注意してもしきれない:
// composed mapper
const f2 = R.compose(mul3, add2);
R.transduce(R.map(f2), xfList, [], [1, 2, 3]); // -> [ 9, 12, 15 ]
// composed transducer. NOTE THE ORDER TO COMPOSE!
const xform = R.compose(R.map(add2), R.map(mul3));
R.transduce(xform, xfList, [], [1, 2, 3]); // -> [ 9, 12, 15 ]
- とはいえ、mapper を compose する方向は、filter が出てくると詰むので、transducer を compose するのがいいだろう:
// composed transducer. compotition of map and filter
const xform2 = R.compose(xform, R.filter(x => x % 2));
R.transduce(xform2, xfList, [], [1, 2, 3]); // -> [ 9, 15 ]
- transformer の形でロジックを書いておく利点は、input や output が変わってもロジック部である transformer の実装をいじる必要がないということ:
// pluggable input
const g = (function* mygen() { yield 1; yield 2; yield 3; }());
R.transduce(xform, xfList, [], g); // -> [ 9, 12, 15 ]
// pluggable output
const xfString = {
'@@transducer/step': (acc, x) => `${acc},${x}`,
'@@transducer/init': () => 'X', // ignored on transduce
'@@transducer/result': x => x,
};
R.transduce(xform, xfString, '', [1, 2, 3]); // -> ',9,12,15'
- 普段は transduce よりも into を使う方が見た目がわかりやすい:
// in most cases, use into in favor of transduce
R.into([], xform, [1, 2, 3]); // -> [ 9, 12, 15 ]
R.into('', xform, [1, 2, 3]); // -> '91215'
R.into(xfList, xform, [1, 2, 3]); // -> [ 9, 12, 15 ]
R.into(xfString, xform, [1, 2, 3]); // -> 'X,9,12,15'