Implementing a conditional / branching Clojure converter

I am trying to make a conditional converter in Clojure as follows:

(defn if-xf "Takes a predicate and two transducers. Returns a new transducer that routes the input to one of the transducers depending on the result of the predicate." [pred ab] (fn [rf] (let [arf (a rf) brf (b rf)] (fn ([] (rf)) ([result] (rf result)) ([result input] (if (pred input) (arf result input) (brf result input))))))) 

This is very useful in that it allows you to do such things:

 ;; multiply odd numbers by 100, square the evens. (= [0 100 4 300 16 500 36 700 64 900] (sequence (if-xf odd? (map #(* % 100)) (map (fn [x] (* xx)))) (range 10))) 

However, this conditional converter does not work very well with converters that perform cleanup on their 1-arity branch:

 ;; negs are multiplied by 100, non-negs are partitioned by 2 ;; BUT! where did 6 go? ;; expected: [-600 -500 -400 -300 -200 -100 [0 1] [2 3] [4 5] [6]] ;; (= [-600 -500 -400 -300 -200 -100 [0 1] [2 3] [4 5]] (sequence (if-xf neg? (map #(* % 100)) (partition-all 2)) (range -6 7))) 

Is it possible to configure an if-xf definition to handle a cleanup case?

I am trying to do this, but with strange behavior:

 (defn if-xf "Takes a predicate and two transducers. Returns a new transducer that routes the input to one of the transducers depending on the result of the predicate." [pred ab] (fn [rf] (let [arf (a rf) brf (b rf)] (fn ([] (rf)) ([result] (arf result) ;; new! (brf result) ;; new! (rf result)) ([result input] (if (pred input) (arf result input) (brf result input))))))) 

In particular, washing occurs at the end of:

 ;; the [0] at the end should appear just before the 100. (= [[-6 -5] [-4 -3] [-2 -1] 100 200 300 400 500 600 [0]] (sequence (if-xf pos? (map #(* % 100)) (partition-all 2)) (range -6 7))) 

Is there any way to make this branching / conditional converter without keeping the entire input sequence in a local state inside this converter (i.e., do all the processing in 1-arity branches when clearing)?

+6
source share
2 answers

The idea is to complete each time the inverter switches. IMO is the only way to do this without buffering:

 (defn if-xf "Takes a predicate and two transducers. Returns a new transducer that routes the input to one of the transducers depending on the result of the predicate." [pred ab] (fn [rf] (let [arf (volatile! (a rf)) brf (volatile! (b rf)) a? (volatile! nil)] (fn ([] (rf)) ([result] (let [crf (if @a? @arf @brf)] (-> result crf rf))) ([result input] (let [p? (pred input) [xrf crf] (if p? [@arf @brf] [@brf @arf]) switched? (some-> @a? (not= p?))] (if switched? (-> result crf (xrf input)) (xrf result input)) (vreset! a? p?))))))) (sequence (if-xf pos? (map #(* % 100)) (partition-all 2)) [0 1 0 1 0 0 0 1]) ; => ([0] 100 [0] 100 [0 0] [0] 100) 
+1
source

I think your question is not defined. What exactly do you want to do when the transducers have a state? For example, what do you expect from this:

 (sequence (if-xf even? (partition-all 3) (partition-all 2)) (range 14)) 

In addition, sometimes the reduction of functions must be performed at the beginning and at the end and cannot be restarted arbitrarily. For example, here is a reducer that calculates the average value:

 (defn mean ([] {:count 0, :sum 0}) ([result] (double (/ (:sum result) (:count result)))) ([result x] (update-in (update-in result [:count] inc) [:sum] (partial + x)))) (transduce identity mean [10 20 40 40]) ;27.5 

Now let's take the average value, where something below 20 is calculated at 20, but everything else decreases by 1:

 (transduce (if-xf (fn [x] (< x 20)) (map (constantly 20)) (map dec)) mean [10 20 40 40]) ;29.25 

My answer is: I think your original solution is the best. It works well using map , as you stated about the usefulness of a conditional converter in the first place.

+1
source

All Articles