transduce に関連する FAQ。 Ramda/transducers-js の実装に引きづられているかも。
transduce と map の違いは?
map は transduce の一種。filter も takeWhile もreduce も transduce の一種。このように transduce は、イテレーション可能なコレクション(典型的にはArray)に対する操作を統一的に理解するための操作。
transduce は何をするの?
入力(典型的にはArray)、(transformerに特徴付けられた)transducer、出力用初期値、出力用reducerを使い、以下の操作をする。
- 出力 acc を出力用初期値で初期化
- 入力から要素をひとつずつ取り出して以下を実施
- 要素 x を transformer で変換し、 x2 にする
- 出力用reducer(acc, x2) で acc を更新
- acc を出力
transducer って何?
複数の transformer を直列化するための関数。
transducer は、transformer を受け取って transformer を返す関数。このようなシグネチャなのでtransducer は composable である。任意の個数の transducer を compose できる。
// 二つの transducer td1 と td2 を compose して新たな transducer td をつくることができる
const td = xf => td1(td2(xf))
// Ramda の compose を使うほうがいいかも
// const td = R.compose(td1, td2)
また、一つ一つのtransducer は transformer で特徴づけられている。transducer を compose することと、 transformer を直列化することは、実装上密接につながっている。
transformer protocol とは?
プログラマが実装する transformer を、transduce の実行過程でどのように利用されるかのインターフェース。具体的には transformer は @@transduce/init
、@@transduce/step
、@@transduce/result
の三つのメソッドを持つ必要がある。
とはいえ、プログラマはこれらのメソッドを実装する必要はない。Ramda や transducers-js などのライブラリを使うと簡単に transformer を作ることができる。いや正確には、 transformer で特徴付けられる transducer を作ることができる。
const R = require('ramda');
const xf = R.map(x => x +1); // returns transducer
実際のプログラミングの流れは?
- 入力が反復可能オブジェクトであることを確認する
- 出力用の初期値と、 reducer を定義する
- 以下の手順で transducer を構築する
- やりたい処理を分解する: op1, op2, op3, ... opN
- 各処理に対する transducerを作る: xf1, xf2, xf3, ... xfN
- transducer を compose する: compose(xf1, xf2, ... , xfN)
- この compose された transducer を使う
transduce は lazy なの?
lazy ではない。 lazy な入力は扱える。
Immutable.js の Seq と似てない?
中間配列が不要という点では、Seq に類似しているが、transduce の方が高機能。transduce の特徴は、むしろ、関心の分離(変形と格納の分離)とそれに付随する再利用性の向上にある。