サーチ…
前書き
トランスデューサは、コンテキストとは独立してデータを処理するための構成可能なコンポーネントです。したがって、入力ソースや出力シンクを知らなくても、コレクション、ストリーム、チャンネルなどを処理することができます。
Clojureコアライブラリは1.7で拡張され、map、filter、takeなどのシーケンス関数はシーケンスなしで呼び出されるとトランスデューサを返すようになりました。トランスデューサは特定のコントラクトを持つ関数であるため、通常のcomp
関数を使用して合成することができます。
備考
トランスデューサは、怠惰が消費されるときに制御することを可能にします。たとえばinto
予想されるように熱心であるが、 sequence
レイジー変換器を介して配列を消費します。ただし、遅延保証は異なります。最初に要素を生成するのに十分なソースが消費されます。
(take 0 (sequence (map #(do (prn '-> %) %)) (range 5)))
;; -> 0
;; => ()
リストが空であるかどうかを判断する:
(take 0 (sequence (comp (map #(do (prn '-> %) %)) (remove number?)) (range 5)))
;; -> 0
;; -> 1
;; -> 2
;; -> 3
;; -> 4
;; => ()
これは通常の遅延的な動作とは異なります。
(take 0 (map #(do (prn '-> %) %) (range 5)))
;; => ()
小さなトランスデューサをベクトルに適用
(let [xf (comp
(map inc)
(filter even?))]
(transduce xf + [1 2 3 4 5 6 7 8 9 10]))
;; => 30
この例では、ローカルに割り当てられたトランスデューサ作成xf
して使用してtransduce
いくつかのデータに適用します。トランスデューサはそれぞれの入力に1を加え、偶数だけを返します。
transduce
はreduce
ようなもので、提供された+
関数を使用して入力コレクションを単一の値に折りたたみます。
これはスレッド最後のマクロのように読み込まれますが、入力データは計算から分離されます。
(->> [1 2 3 4 5 6 7 8 9 10]
(map inc)
(filter even?)
(reduce +))
;; => 30
トランスデューサの適用
(def xf (filter keyword?))
コレクションに適用し、シーケンスを返します。
(sequence xf [:a 1 2 :b :c]) ;; => (:a :b :c)
コレクションに適用し、結果のコレクションを別の関数で減らします。
(transduce xf str [:a 1 2 :b :c]) ;; => ":a:b:c"
コレクションに適用し、結果を別のコレクションにconj
する:
(into [] xf [:a 1 2 :b :c]) ;; => [:a :b :c]
トランスデューサを使用してメッセージをフィルタリングするコア非同期チャネルを作成します。
(require '[clojure.core.async :refer [chan >!! <!! poll!]])
(doseq [e [:a 1 2 :b :c]] (>!! ch e))
(<!! ch) ;; => :a
(<!! ch) ;; => :b
(<!! ch) ;; => :c
(poll! ch) ;;=> nil
トランスデューサの作成/使用
したがって、Clojureのマップとフィルタで最もよく使用される関数は、コレクションで呼び出されなければトランスデューサ(合成可能なアルゴリズム変換)を返すように変更されています。つまり、
(map inc)
はトランスデューサを返します(filter odd?)
そうではありませ(filter odd?)
利点:compは関数を1つの関数にまとめることができます。これはコレクションを1回だけトラバースすることを意味します。一部のシナリオで実行時間を50%以上節約します。
定義:
(def composed-fn (comp (map inc) (filter odd?)))
使用法:
;; So instead of doing this:
(->> [1 8 3 10 5]
(map inc)
(filter odd?))
;; Output [9 11]
;; We do this:
(into [] composed-fn [1 8 3 10 5])
;; Output: [9 11]