Clojure-トランスデュヌサヌ、枛速機、その他のかす

最近、 トランスデュヌサヌは名声を埗たした-Clojure 1.7の新機胜で、ただリリヌスされおいたせん。 執筆時点では、Clojure 1.7-alpha5が関連しおいたすが、 Python 、 Ruby 、 JavaScript 、 PHP 、 Java 、 C ++ 、 Lua 、 Erlangなどのさたざたな蚀語でかなりの数のトランスデュヌサヌポヌトが既に登堎しおいたす。 そしお...それは少しがっかりです。 結局、かなり前にClojure 1.5に戻っお、 reducersラむブラリが远加されたした 。だから、 リデュヌサヌに぀いおは誰も話さず、䜕も移怍したせんでした。

Clojureでこれらすべおの枛速機ずトランスデュヌサヌが必芁だった理由を芋おみたしょう本圓に必芁なのでしょうか、それらがどのように機胜し、どのように䜿甚するのか...

この蚀語のコンテキスト倖でClojureに由来する抂念を蚘述するのは間違っおいたす。 したがっお、Clojureには倚くのリストがありたす。 しかし、マタンは​​ありたせん。 䞀般に、Clojureの初期知識特にシヌケンスの抂念は適切ですが、Haskellを知る必芁はありたせん。 たた、リストされおいる暙準関数のリストはすべお実際には非垞に倉曎されおおり、時には「わずかに」壊れおいるこずも事前に譊告したす。 簡玠化のためにすべお。 そうそう、写真は同じブリトヌです。

オフにしたす...


そのため、Clojureは関数型蚀語です。぀たり、 通垞の 呜什型 サむクルは良くありたせん。
さお、倧䞈倫、私たちは本圓に望んでいたせんでした-機胜的に楜しい還元がありたす

(defn my-reduce ([rf coll] ;;     (if-let [s (seq coll)] (my-reduce rf (first s) (next s)) (rf))) ([rf acc coll] (if-let [[x & xs] (seq coll)] (recur rf (rf acc x) xs) acc))) 

実際には、 reduceはもちろん、 少し異なる方法で実装されたすが、今は重芁ではありたせん。忘れおはいけたせん。 rf関数reduct関数ず呌びたしょうは、ここで2぀の匕数を取りたす。最初の匕数は、䞀皮の「移動状態」です。 2番目はcollシヌケンスの芁玠です。 初期状態が指定されおいない堎合、 (first coll)たたは(rf)たす。 状態accを「ドラッグ」しながら、 rfず呌ぶ各芁玠に぀いお、コレクションcoll党䜓をaccたす。 芁玠が完成したら、 acc返したす。

小さな䟋。 文字列のリストがあり、それらの合蚈の長さを蚈算するずしたす。
ルヌプを䜿甚した呜什コヌドは次のずおりです。

 (defn length-of-strings [strings] (with-local-vars [acc 0] ;; -,  Clojure   ! (doseq [c strings] (var-set acc (+ @acc (count c)))) ;;    @acc)) 

ルヌプ状態は、単玔なacc 数倀カりンタヌです。 各反埩で、 (+ @acc (count c))に等しく蚭定したす。
そしおもう1回、 reduceのみをreduceたす。

 (defn length-of-strings [coll] (my-reduce (fn ([acc c] (+ acc (count c)))) ;;  - 0 ;;   coll)) 

䞀時的に怠lazを「忘れる」堎合、 mapやfilterなどの倚くのプリミティブな操䜜を実装できfilter 。

 (defn my-map [f coll] (my-reduce (fn [acc c] (conj acc (fc))) [] coll)) (defn my-filter [p coll] (my-reduce (fn [acc c] (if (pc) (conj acc c) acc)) [] coll)) 

takeを実装take指定reduceれたreduceオプションは機胜しなくなりたす-サむクルは垞にシヌケンス党䜓を実行したすこれは、すべおが遅延しおいるHaskellの䞀皮ではありたせん。

この欠点を克服するために、バヌゞョン1.5では特別な束葉杖マヌカヌを远加し、察応する述語をreduced? 。 同時に、 reduceを曞き盎し、次のようなものを埗たした

 (defn my-reduce ([rf coll] (if-let [s (seq coll)] (my-reduce rf (first s) (next s)) (rf))) ([rf acc coll] (if-let [[x & xs] (seq coll)] (let [ret (rf acc x)] (if (reduced? ret) @ret (recur rf ret xs))) acc))) 

reduct関数が返されるず(reduced ...) 、ルヌプが䞭断し、倀@retたす。

 (defn take-r [n coll] (my-reduce (fn [[n1 acc] c] (if (pos? n1) [(dec n1) (conj acc c)] (reduced acc))) [n []] coll)) ;;    ! (take-r 5 (range)) ;; => [0 1 2 3 4] 

玠晎らしい削枛機胜を思い出さずにはいられたせん。 本質的に、これはreduce類䌌物であり、最埌だけでなくaccすべおの䞭間倀の遅延リストのみを返したす。 デバッグ時に䜿甚するず非垞に䟿利です。 アルゎリズムステップを関数ずしお蚘述し、入力デヌタをreduceしおコレクションに察しおreduceを実行したす。 䜕かが間違っおいる堎合は、 reduceをreductionsに眮き換え、REPLで実行し、すべおの䞭間ステップを取埗したす。 サむクルでは、それほど簡単ではありたせん-デバッグ束葉杖を修正する必芁がありたすが、これはあたり䟿利ではありたせん。

削枛はそれ自䜓で有甚である可胜性がありたす;階乗は蚈算するためにありたす

 ;; => **   (def factorials (reductions *' (cons 1 (map inc (range))))) (nth factorials 20) ;; => 2432902008176640000 

Clojureはシヌケンスを䜿甚しおコレクションを埪環したす。 ベクトル、ハッシュテヌブル、たたは単玔なむテレヌタを調べる堎合、倧量の䞀時オブゞェクトがヒヌプ内に䜜成されたす。

このような状況で芁求される明らかな最適化は、これが理にかなっおいるコレクションに察しお特別なreduceオプションを実装するこずです。 コレクションがそのような最適化に圹立たない堎合は、蚘事の冒頭で瀺したものず同様の暙準実装を䜿甚しおください。 これを行うには、特別なプロトコルclojure.core.protocol / CollReduceがありたす。 コレクションオブゞェクトがサポヌトする堎合、この実装はclojure.core/reduce内で䜿甚されたす。 したがっお、Clojureのreduceは通垞、同様のdoseqルヌプよりも高速です。

倉圧噚


トランスフォヌマヌは、1぀のリダクト関数を取り、新しいリダクト関数を返す関数です。
たずえば、「1ず぀増加する」トランスフォヌマヌを次に瀺したす。

 (defn inc-t [rf] (fn [acc c] (rf acc (inc c)))) ;;     (reduce + 0 (map inc [1 2 3 4])) ;; => 14 (reduce (inc-t +) 0 [1 2 3 4]) ;; => 14 

この問題を倚少䞀般化しお、 inc代わりに関数を指定するこずができたす。

 (defn map-t [f] (fn [rf] (fn [acc c] (rf acc (fc))))) (def inc-t (map-t inc)) (def dec-t (map-t dec)) ;; ... (reduce (inc-t +) 0 [1 2 3 4]) ;; => 14 

そしお、ここで、䟋えば、トランスフォヌマヌ「フィルタヌ」

 (defn filter-t [pred] (fn [rf] (fn [acc c] (if (pred c) (rf acc c) acc)))) (def odd?-t (filter-t odd?)) (def even?-t (filter-t even?)) ;;  (reduce (even?-t *) 1 [1 2 3 4]) ;; => 8 

耇数の倉圧噚を組み合わせるこずは可胜ですか もちろん

 (defn odd?-inc-t [rf] (odd?-t (inc-t rf))) ;; ..    (def odd?-inc-t (comp (filter-t odd?) (map-t inc))) ;;   .. (def odd?-inc-t (comp (fn [rf] (fn [acc c] (if (odd? c) (rf acc c) acc))) (fn [rf] (fn [acc c] (rf acc (inc c)))))) ;;      (defn odd?-inc-t [rf] (fn [acc c] (if (odd? c) (rf acc (inc c)) acc))) ;;   (reduce * 1 (->> [1 2 3 4 5] (filter odd?) (map inc))) ;; => 48 (reduce (odd?-inc-t *) 1 [1 2 3 4 5]) ;; ==> 48 

トランスフォヌマヌが「逆」の順序で進むこずに泚意する䟡倀がありたす。 コレクションの芁玠をBに到達する前にトランスフォヌマAで凊理したい堎合、 (comp AB)ようにそれらを接着する必芁がありたす。 そしお今、トリック

 (def cc (vec (range 1000000))) (time (reduce + 0 (->> cc (filter odd?) (map inc)))) ;; "Elapsed time: 171.390143 msecs" ;; => 250000500000 (time (reduce ((comp (filter-t odd?) (map-t inc)) +) 0 cc)) ;; "Elapsed time: 93.246015 msecs" ;; => 250000500000 

方法は次のずおりです、明癜な速床の急激な増加 もちろん、すべおは倚くの詳现ずさたざたなニュアンスに䟝存するため、実際にはゲむンは異なる堎合がありたす。 䞀般に、このコヌドをベンチマヌクずしお䜿甚するべきではないず蚀いたいです。

しかし、党䜓ずしお、結果は驚くにはほど遠いです。 mapずfilterを䜿甚mapず、2぀の䞭間シヌケンスが䜜成されたす。 元のベクトルを実行し、フィルタヌ凊理された倀の䞀時的なリストを䜜成したす。 次に、このリストを調べお、芁玠を拡倧した別のリストを䜜成したす。 そしお最埌に、倀をたずめお、すでに説明したす。

䞀方、トランスフォヌマヌを䜿甚したオプションでは、䞀時的なコレクションは䜜成されたせん。 代わりに、 odd?゜ヌス芁玠にすぐに適甚されodd? 、およびinc 。

私の枛速機はどこにありたすか


そしお、バヌゞョン1.5が新しい暙準ラむブラリclojure.core.reducers導入するたで、すべおは順調clojure.core.reducers 。 そうです、 別のラむブラリを明瀺的にむンポヌトする必芁がありたす。 たた、 map 、 filter 、 take-whileなどのバヌゞョンも発衚したした。 そしお、もちろん、それらはclojure.core通垞のバヌゞョンず互換性がありたせん。 したがっお、単玔な(use 'clojure.core.reducers)代わりに(use 'clojure.core.reducers) (require '[clojure.core.reducers :as r])ず蚘述する方が適切です。

では、枛速機ずは䜕ですか 簡単に蚀うず、愚かなこずですレデュヌサヌずは、削枛できるオブゞェクトのこずです。 clojure.core.reducers芳点でのコレクションはすべおレデュヌサヌです。 ハッシュテヌブルはレデュヌサヌです。 java.lang.Stringレデュヌサヌ。 たあ、もちろん、 nil 。 定矩を芋おみたしょう

 (defn reducer [coll xf] ;; `xf` -   (reify clojure.core.protocols/CollReduce (coll-reduce [this f1] (let [f2 (xf f1)] (clojure.core.protocols/coll-reduce coll f2 (f2)))) (coll-reduce [this f1 init] (let [f2 (xf f1)] (clojure.core.protocols/coll-reduce coll f2 init))))) 

ここでcollコレクションが取埗され、新しいコレクションが返されたす。そこで、 reduceを実行できたす。 芁玠を远加したり、削陀したり、芁玠を調べたりするこずもできたせん。 ただし、 reduce開始する前reduce reduct関数はxfトランスフォヌマヌを通過したす。

 (def nums [1 2 3 4 5]) (def nums+1 (reducer nums inc-t)) (reduce + 0 nums) ;; => 15 (reduce + 0 nums+1) ;; => 20 

すでに述べたように、reducersラむブラリは独自のオプションmap 、 filter 、 take-whileなどを発衚しおいたす。 それらはすべお枛速機を受け入れ、察応する倉圧噚が「取り付けられおいる」新しい枛速機を返したす。

これはclojure.core.reducers/mapようにclojure.core.reducers/map もちろん、芋た目は倧きく異なりたす 。

 (def map-r [f coll] (reducer coll (map-t f))) 

そしお今、これらすべおがどのように䜿甚できるかのいく぀かの䟋

 (require '[clojure.core.reducers :as r]) (def nums [1 2 3 4 5 6 7 8]) (type (map inc nums)) ;; => clojure.lang.LazySeq (reduce conj [] (map inc nums)) ;; => [2 3 4 5 6 7 8 9] (type (r/map inc nums)) ;; => clojure.core.reducers$folder$reify__1234 ;; -  sequence (reduce conj [] (r/map inc nums)) ;; => [2 3 4 5 6 7 8 9] ;;      (reduce conj [] (r/filter odd? nums)) ;; => [1 3 5 7] (reduce + 0 (->> nums (r/map inc) (r/map inc))) ;; => 52 ;; ~~ (+ 0 (inc (inc 1)) (inc (inc 2)) ...) (reduce + 0 (->> nums (r/filter odd?) (r/map inc))) ;; => 20 ;; ~~ (+ 0 (inc 1) (inc 3) ...) 

平行


正盎に蚀うず、いわゆる「リデュヌサヌ」は無駄です。 「フォルダ」の方が正しいでしょう。 実際、 CollReduceプロトコルレデュヌサヌよりもずっず前に登堎したに加えお、別のより重芁なCollFoldプロトコルがラむブラリで宣蚀されおいたす。

 (defprotocol CollFold (coll-fold [coll n combinef reducef])) 

原則ずしお、これは非垞に䌌おおり、還元関数のみが2぀になり、わかりにくい匕数nも远加されたした。 アむデア䞀郚のコレクションは耇数のスレッドで実行できたす。 簡単に説明するず、サむズを玄n芁玠のブロックに分割し、各ピヌスを#(reduce reducef (combinef) %)を䜿甚しお折りたたんでから、結果リストブロックごずに1぀を再び#(reduce combinef %)が、 #(reduce combinef %)たす。

䞊行しお折りたたむこずができるレデュヌサヌは、 フォルダヌず呌ばれたす 。
CollFoldプロトコルをサポヌトしおいるのは、ベクタヌずハッシュテヌブルの2぀の暙準コレクションのみです。

 (def v (vec (range 10000000))) ;; ,  1  (time (reduce + v)) ;; "Elapsed time: 648.897616 msecs" ;; => 49999995000000 ;;    (time (r/coll-fold v 512 + +)) ;; "Elapsed time: 187.414147 msecs" ;; => 49999995000000 

これが理にかなっおいるすべおの暙準CollFold実装しCollFold 。 これは、たずえば、 r/map 、 r/filter 、 r/mapcat 、 r/flattenです。 䞀方、 r/take 、 r/take-while 、 r/dropは䞊列化をサポヌトしおいたせん。 䞊蚘はr/map実装でした。 圌女の曎新されたバヌゞョンは次のずおりです。

 (def map-r [f coll] ;;   `reducer`  `folder` (folder coll (map-t f))) 

coll-fold盎接䜿甚する必芁はありたせん-日垞のニヌズに合わせた折りたたみラッパヌがありたす。 nのデフォルト倀ブロックサむズを512に蚭定したす。䞀般に、ヒントは明確です-レデュヌサヌは、倧芏暡なコレクション> 1Kアむテムを察象ずしおいたす。 たた、 coll-fold盎接䜿甚しないで、 coll-foldを呌び出したす。

ああ、ただfoldcatがありたす。 䞀皮の高速化マルチスレッドによるオプション#(reduce conj [] %) 。 この関数は 、 Counted 、 Sequable 、およびCollFoldを実装するclojure.core.reducers.Catオブゞェクトを返したす 。

 (r/map inc [1 2 3]) ;; => #<reducers$folder$reify__..... ;;   ** ? ;; ..     `reduce`  (reduce conj [] (r/map inc [1 2 3])) ;; => [2 3 4] ;;    ... (def v (vec (range 1000000))) (time (count (reduce conj [] (r/map inc v)))) ;; "Elapsed time: 90.124397 msecs" ;; => 1000000 ;; -  ,    `foldcat` (time (count (r/foldcat (r/map inc v)))) ;; "Elapsed time: 25.054988 msecs" ;; => 1000000 (time (count (r/foldcat (r/map inc (r/foldcat (r/map inc v)))))) ;; "Elapsed time: 32.054988 msecs" ;; => 1000000 ;;  `foldcat`, ,  foldable (, ) (satisfies? r/CollFold (r/foldcat [])) ;; => true 

圌らはシヌンに突入し......


枛速機ずは異なり、トランスデュヌサヌは独立したラむブラリではありたせん。 clojure.coreモゞュヌルに盎接統合されるのは、むしろ抂念アむデアを読んでくださいです。 バヌゞョン1.7でこの歓迎を埅っおいたす少し残っおいたす。

簡単に説明するず、トランスデュヌサヌは同じ名前のトランスブランドですが、ブランド名が倉曎された埌にのみ名前が倉曎されたす。 さお、ほずんどこれから、還元関数は0ず2の匕数だけでなく、1を取るこずもできたす。トランスデュヌサは、それぞれ0-1-2-ary還元関数から新しい0-1-2-ary還元関数ぞの関数です。 。

 (def typical-transducer (fn [rf] (fn ([] ...) ;;    ([acc] ...) ;; ... ([acc c] ...))) ;; ,    ,    ;;   `map-t`,  33%   (defn map-t-improved [f] (fn [rf] (fn ([] (rf)) ;;   ([acc] (rf acc)) ;;   ([acc c] (rf acc (fc)))))) ;;  `c`  `(fc)` 

初期芁玠が必芁な堎合は、前述のように0項還元関数を呌び出すこずができたす。 実際、2進バリアントは、削枛自䜓に䜿甚されたす。 1項オプションは、ゞョブ党䜓の最埌で reduce完了時に呌び出されたす。 最埌の埌に新しい芁玠を「远加」する必芁がある堎合に必芁です。

䟋コレクションからの繰り返しをスキップする重耇排陀トランスデュヌサヌ

 (defn my-dedupe [] (fn [rf] ;; , ! (let [prev (atom ::none)] (fn ;;   - ([] (rf)) ([acc] (rf acc)) ([acc c] (let [p @prev] (reset! prev c) (if (= pc) acc (rf acc c)))))))) (def rf ((my-dedupe) +)) (reduce rf 0 [1 1, 2 2, 3, 1 1]) ;; => 7 (reduce rf 0 [1 1, 2 2, 3, 1 1]) ;; => 6 ;; ... `rf`  ,    2  

埮劙な点は、トランスデュヌサヌが新しい還元関数を返すこずです。 さらに、この還元関数は可倉状態を持ち、実際には3぀の異なるこずを実行できたすアリティごずに1぀。 たあ、ある皮のオブゞェクト。 しかし同時に、トランスデュヌサヌ自䜓には状態がなく、䞀皮の工堎ずしおのみ機胜したす。

reduct関数の1進バリアントの䜿甚䟋ずしお、 partition-allが指定されおいたす。 簡玠化された実装

 (defn partition-all-t [n] (fn [rf] (let [buffer (java.util.ArrayList. n)] ;; ! (fn ([] (rf)) ([acc] (if (.isEmpty buffer) ;;    -   (rf acc) ;; ... (let [v (vec (.toArray buffer)) ;;     acc' (rf acc v)] ;;    2- `rf` ;;       (rf acc')))) ([acc c] (.add buffer c) (if (= n (.size buffer)) ;;    - ""  (let [v (vec (.toArray buffer))] (.clear buffer) (rf acc v)) ;;  -    acc)))))) ;;  ,   (   ,  (conj) => []) (reduce ((partition-all-t 3) conj) (range 10)) ; >> ClassCastException java.lang.Long cannot be cast to clojure.lang.IPersistentCollection ;;  ... ;;  ,    []... (reduce ((partition-all-t 3) conj) [] (range 10)) ;; => [[0 1 2] [3 4 5] [6 7 8]] ;; ,  ... 

うヌん... 0項オプションも1項オプション((partition-all-t 3) conj)も呌び出されたせんでした-通垞のreduceは、これらすべおの革新に぀いお䜕も知りたせん。 コレクションが空の堎合にのみ0-aryオプションを呌び出し、1-aryオプションはたったく呌び出したせん。

したがっお、圌らは新しいtransduce関数を䜜成したした。 ここでは、「廃止された」 reduceずは異なり、初期状態が明瀺的に蚭定されおいない限り、垞に(rf)呌び出したす。 そしお、この関数は(rf acc) 、そしお䞀床だけ呌び出すこずが保蚌されおいたす。 そしお、 transduce自䜓が倉換噚を呌び出し、可倉還元機胜を目から隠したす。 蚀い換えれば、すべおの汚い䜜業副䜜甚の芳点からは「内郚」で行われたす。

 ;;    ,      (transduce (partition-all-t 3) conj (range 10)) ;; => [[0 1 2] [3 4 5] [6 7 8] [9]] ;; ... ! ;;   ( ) (transduce (comp (filter odd?) (partition-all-t 3)) conj (range 10)) ;; => [[1 3 5] [7 9]] 

しかし、 reduce代わりにtransduceを䜿甚しようずするずどうなりたすか

 (reduce (identity -) 0 [1 2 3 4]) ;; => -10 ;; ~~ (- (- (- (- 0 1) 2) 3) 4) (transduce identity - 0 [1 2 3 4]) ;; => 10 ;;  ! 

reduceをtransduceに盎接眮き換えるこずは䞍可胜であるこずが刀明したした-1項還元関数の新しい芁件が干枉したす。 この䟋では、蚈算が完了した埌、呌び出しをtransduce (- acc) 、結果の笊号を逆にしたす。 完了するず、状況を修正できたす。

 ((completing -) 3 2) ;; => 1 ((identity -) 1) ;; => -1 ((completing -) 1) ;; => 1 (transduce completing - 0 [1 2 3 4]) ;; => -10 ;;   ! 

トランスフォヌマヌ、トランスデュヌサヌを操䜜するための蚀語の䞭栞には、特別な機胜が登堎しおいたす。 たた、これらの非垞にトランスデュヌサヌの暙準のセットを远加するこずが期埅されたす。 そしお、倚くの新しい関数を生成しないようにそれらが非垞に倚いため、2぀のアカりントで混乱したす、既存のmap 、 filter 、 take 、 mapcat 、 mapcatおよびcompanyをアップグレヌドするこずにしたした。

 (map inc [1 2 3]) ;; => (2 3 4) (map inc) ;; => #<core$map$fn__4525 clojure.core$map$fn__4525@2d9e9038> ;;  ! ;;    (transduce (map inc) + [1 2 3]) ;; => 9 (transduce (comp (map inc) (filter even?)) + [1 2 3]) ;; => 6 ;; ~~ (+ (inc 1) (inc 3)) => 6 

transduce加えお、 transduce操䜜するための機胜がいく぀かありたす。

 ;;     (sequence (map inc) [1 2 3]) ;; => (2 3 4) ;;     (transduce (map inc) conj [1 2 3]) ;; => [2 3 4] ;; ... ;;  `sequence`   ** ! ;;  `transduce`      (take 5 (sequence (map inc) (range))) ;; => (1 2 3 4 5) ;;   `into`     (into [9] (map inc) [1 2 3]) ;; => [9 2 3 4] 

しかし、最も面癜い機胜は教育です。 seqを呌び出しおjava-iteratorをreduceか取埗できるプロキシオブゞェクトを返したす。 このオブゞェクトは、単にtransduceたたはsequnce呌び出すこずが期埅されおいたす。 些现なこずですが、䟿利です。

 (def odds (eduction (filter odd?) (range))) (def evens (eduction (remove odd?) (range))) ;;     sequential (take 5 odds) ;; => (1 3 5 7 9) ;;     sequence   100500  ;;       - sequence  GC (nth odds 100500) ;; => 2010001 ;;       reduce (  LazyCol) ;; ~= (reduce ((filter even?) ((take 100500) +)) 0 (range)) (transduce (take 100500) + evens) ;; => 10100149500 

停止、停止、停止。 clojure.core.reducers/reducer思わせたすが、最小化するこずしかできず、 seq実行seq蚱可seqれおいたした。 r/reducerはゎミ箱に捚おたす しかし、 r/folderではなく、圌はマルチスレッドの方法を知っおいたす

 (require '[clojure.core.reducers :as r]) (def v (vec (range 1000000))) (time (transduce (map inc) + v)) ;; "Elapsed time: 120.193971 msecs" ;; => 500000500000 (time (r/fold + (r/folder v (map inc)))) ;; "Elapsed time: 37.597224 msecs" ;; => 500000500000 ;;   ! (transduce (take 100500) + v) ;; => 5050074750 (r/fold + (r/reducer v (take 100500))) ;; => 5050074750 ;;  ;; reducer  -   eduction (r/fold + (eduction (take 100500) v)) ;; => 5050074750 (reduce + (r/folder v (take 100500))) ;; => 5050074750 ;;    (r/fold + (r/folder v (take 100500))) ;; => 109071345018 ;; ... ;;     (   ) 

トランスデュヌサヌを䜿甚するず、通垞のmap / filter / etc 遅延シヌケンスに基づくず比范しお優れたパフォヌマンスず、柔軟性/抜象性の䞡方が実珟したす。 ここでは、clojurシヌケンスに぀いお説明しおいるこずに泚意しおください。抜象化ず速床の点では、トランスデュヌサは通垞のむテレヌタ/列挙子/ゞェネレヌタに匹敵したす異なる蚀語では異なる呌び方です。

しかし、Clojureに戻りたす。 以前のcore.asyncには、 map> 、 map< 、 filter< 、 filter>などのような倚くの関数がありたした。 今日、それらは削陀されたした 削陀されたので、これたで停止されただけです。 しかし、圌らは、チャンネルを䜜成するずきにトランストランスデュヌサヌを指定するこずを蚱可したした

 ;;      project.clj (require '[clojure.core.async :as a]) ;;     (def xf (filter odd?)) ;;     (def ch (a/chan 10 xf)) ;;      0  9     (a/onto-chan ch (range 10)) ;;     (a/<!! (a/into [] ch)) ;; => [1 3 5 7 9] 

トランスデュヌサヌは、緩衝されたチャンネルにのみ掛けるこずができたす。 そしお、芁玠がバッファに衚瀺される前に、トランスデュヌサはそれを凊理したす。 あらゆる皮類のパむプラむンがあり、トランスデュヌサヌでも動䜜したす。

たずめるず


さたざたなレゞュヌサヌ/トランスデュヌサヌはすべお、畳み蟌み挔算の䞀般化です。 したがっお、それらを䜿甚するには、2぀の匕数を持぀還元関数が必芁です。

2進オプションに加えお、0進オプションも決定するこずをお勧めしたす。畳み蟌みの初期状態が指定されおいない堎合に䜿甚できたす。 たたは、䜿甚されない堎合がありたす。元のコレクションが空でない堎合、 reduceは最初の芁玠を取りたす。 しかし、 transduceはそれほど意味のある動䜜をしたせん-初期状態が明瀺的に枡されるか、reduct関数の0項呌び出しが䜿甚されたす。

䞀方、 transduceは還元関数からより倚くを必芁ずしたす-1項オプションが必芁です。 これは、䞀般的な堎合、ほずんど䜕もしたせん。 真剣に、通垞、この堎合は([x] x)が最も意味のある実装です。 しかし、私たちは怠け者で、叀い関数0/2項を曞き換えるのが面倒なので、空の1項オプションを远加するcompletingラッパヌを䜿甚したす。

さらに、枛速機は倉圧噚に基づいおいたす。 Transformer = rf -> rfタむプの関数。 実際、枛速機は、倉圧噚がしっかりずねじ蟌たれたコレクションです。 そしお、このコレクションでreduceを実行reduceたびに、最初にトランスフォヌマヌがreduct関数を「台無しにしたす」。

倉換噚〜=倉換噚、1項還元関数のサポヌトのみが必芁です。 したがっお、この最も䞍幞な1-arnikを垞に定矩し、誇らしげに宣蚀したす。「もちろん、叀いトランスは䜿甚せず、トランスデュヌサヌのみを䜿甚したす」。

このすべおにより、トランスデュヌサヌはコレクションの操䜜に限定されたせん。 それらをチャネル、入出力ストリヌム、キュヌ、オブザヌバヌなどに固定できたす。 䞀般に、そのすべおのファンタゞヌには十分です。

合蚈

Source: https://habr.com/ru/post/J247889/


All Articles