入門Transducers
Clojure 1.7勉強会
@athos0220
Transducerとは?
Transducers are composable algorithmic transformations.
They are independent from the context of their input and
output sources and specify only the essence of the
transformation in terms of an individual element. Because
transducers are decoupled from input or output sources,
they can be used in many different processes - collections,
streams, channels, observables, etc.
http://clojure.org/transducers
なるほど分からん
つまり、、、
Transducerは合成可能なアルゴリズム的変換
入力源や出力先に依らず、各要素を変換する
…やっぱり分からん
Transducerが生まれた
経緯を知ると理解が深まる
Transducerが生まれた
経緯を知ると理解が深まる
…かも?
おさらい:シーケンス処理
(->> (range 5)
(map inc)
(filter odd?)
(reduce + 0))
(->> (range 5)
(map inc)
(filter odd?)
(reduce + 0))
(->> (range 5)
(map inc)
(filter odd?)
(reduce + 0))
(->> (range 5)
(map inc)
(filter odd?)
(reduce + 0))
各要素に1を足し
(->> (range 5)
(map inc)
(filter odd?)
(reduce + 0))
各要素に1を足し
奇数以外を取り除き
(->> (range 5)
(map inc)
(filter odd?)
(reduce + 0))
各要素に1を足し
奇数以外を取り除き
足し合わせる
0 1 2 3 4(range 5)
0 1 2 3 4
51 2 3 4
(range 5)
(map inc)
inc inc inc inc inc
0 1 2 3 4
51 2 3 4
51 3
(range 5)
(map inc)
(filter odd?)
inc inc inc inc inc
odd? odd? odd? odd? odd?
0 1 2 3 4
51 2 3 4
51 3
(range 5)
(map inc)
(filter odd?)
9
inc inc inc inc inc
odd? odd? odd? odd? odd?
(reduce + 0)
++ +
おさらい:Reducers
(->> (range 5)
(r/map inc)
(r/filter odd?)
(reduce + 0))
(range 5) 0 1 2 3 4
(range 5)
(r/map inc) 0
要素に1足して処理
1 2 3 4
0 1 2 3 4
Reducible
(range 5)
(r/map inc)
(r/filter odd?)
0
要素に1足して処理
1 2 3 4
0 1 2 3 4
0
要素に1足して処理
奇数ならば処理
1 2 3 4
Reducible
Reducible
(range 5)
(r/map inc)
(r/filter odd?)
(reduce + 0)
0
要素に1足して処理
1 2 3 4
0 1 2 3 4
9
0
要素に1足して処理
奇数ならば処理
1 2 3 4
要素に1足して処理
奇数ならば処理
+
Reducible
Reducible
おさらい:Reducers
Reducerでは、シーケンス処理と異なり、各
ステップで要素毎の中間データを作らない
reduceする段階で各要素に処理を適用する
(本来はさらにfork/joinを使った並列処理に発展するけどここでは割愛)
で、     って
具体的に何?
要素に1足して処理
(reduce + 0 (map inc s))
(reduce + 0 (map inc s))
(reduce + 0 (map inc s))
(reduce + 0 (map inc s))
「各要素を1足して、
先頭から順に要素を足し合わせる」
(reduce + 0 (map inc s))
「各要素を1足して、
先頭から順に要素を足し合わせる」
(reduce (fn [a x] (+ a (inc x))) 0 s)
「先頭から順に各要素に1足した値を足し合わせる」
これは以下と等価
(reduce (fn [a x] (+ a (inc x))) 0 s)
(reduce (fn [a x] (+ a (inc x))) 0 s)(fn [a x] (+ a (inc x)))
これが      の正体要素に1足して処理
(fn [a x] (+ a (inc x)))
同じように
(reduce + 0 (filter odd? s))
(reduce + 0 (filter odd? s))
(reduce + 0 (filter odd? s))
(reduce + 0 (filter odd? s))
「奇数以外の要素を取り除き、
先頭から順に要素を足し合わせる」
(reduce + 0 (filter odd? s))
「奇数以外の要素を取り除き、
先頭から順に要素を足し合わせる」
(reduce (fn [a x]
(if (odd? x) (+ a x) a))
0 s)
「先頭から順に奇数の要素を足し合わせる」
これは以下と等価
(reduce (fn [a x]
(if (odd? x) (+ a x) a))
0 s)
(reduce (fn [a x]
(if (odd? x) (+ a x) a))
0 s)
(fn [a x]
(if (odd? x) (+ a x) a))
これが      の正体奇数ならば処理
(fn [a x]
(if (odd? x) (+ a x) a))
一般的に
(reduce op z (map f s))
(reduce (fn [a x] (op a (f x))) z s)
=
一般的に
(reduce op z (filter f s))
(reduce (fn [a x] (if (f x) (op a x) a)
z s)
=
これらは各シーケンス処理の
本質的な部分を表すものとして
標準の関数から取得できるようになった
(map f) = (fn [op]
(fn [a x]
(op a (f x)))
(filter f) = (fn [op]
(fn [a x]
(if (f x) (op a x) a)))
これらは各シーケンス処理の
本質的な部分を表すものとして
標準の関数から取得できるようになった
(map f) = (fn [op]
(fn [a x]
(op a (f x)))
(filter f) = (fn [op]
(fn [a x]
(if (f x) (op a x) a)))
↑これがTransducer!!
= (map inc)要素に1足して処理
奇数ならば処理
= (filter odd?)
Transducerの実体はただの関数なので
関数合成により合成できる
要素に1足して処理
奇数ならば処理
+
要素に1足して処理
奇数ならば処理
(comp (map inc)
(filter odd?))
合成したものもTransducer
0
要素に1足して処理
奇数ならば処理
1 2 3 4
Reducible
0
要素に1足して処理
奇数ならば処理
1 2 3 4
Reducible
Reducible = Transducer + コレクション
0
要素に1足して処理
奇数ならば処理
1 2 3 4
Reducible
Reducible = Transducer + コレクション
逆に、Transducerとは
Reducibleからコレクションへの依存を除き
汎用的に使えるようにしたものといえる
さまざまなTransducer
多くの標準シーケンス処理関数が1.7以降Transducer
を返すようになる
map
mapcat
filter
remove
take
take-while
drop
drop-while
take-nth
replace
partition-by
partition-all
keep
keep-indexed
map-indexed
distinct
interpose
Transducerを使うときの注意点
関数合成の順序が通常と逆になる
内部状態をもつTransducerがあるため、
Transducerは使い回さない
Transducerを引数にとるAPI
(transduce xf f init s)
= (reduce (xf f) init s)
(into to xf from)
コレクションfromをtransducerで変換しながらコレ
クションtoに変換
(eduction xf coll)
transducerとコレクションからReducibleを生成
core.asyncでの利用
(def c (async/chan
(comp (map inc)
(filter odd?))))
(async/onto-chan c (range 5))
(async/<!! c) ;=> 1
(async/<!! c) ;=> 3
(async/<!! c) ;=> 5
対象のデータ構造に依らないためチャネルからも使える
まとめ
Transducerは対象とするデータ構造に依らな
いアルゴリズム的変換を記述する標準的な方法
中間データを生成しないため、従来のシーケン
ス処理よりも高速化できる可能性も

入門Transducers