Re: Opinion on take-while for stateful transducers
Thanks for the reply. I do like this. I think it's actually more elegant. Definitely going into my toolbox. One thing I dislike is that I still have to re-implement the logic of the already existing (performant tested) transducers. Also, I could also add a 4th parameter: 'terminate-early?' but that's then already 4 parameters to remember... I'll have to see what way is best once I implement more transducers with your or my (hacky) method. Cheers On Monday, May 11, 2015 at 1:34:31 PM UTC-4, miner wrote: Not an expert, but I’ll throw out an alternative approach that might work for you. I think it’s simpler to use a transducer that calls functions rather than trying to transform an existing transducer to do the cutoff. (defn take-while-accumulating [accf init pred2] (fn [rf] (let [vstate (volatile! init)] (fn ([] (rf)) ([result] (rf result)) ([result input] (if (pred2 @vstate input) (do (vswap! vstate accf input) (rf result input)) (reduced result))) accf is like a reducing function: takes two args, state and input, and returns new state of the “accumulation”. init is the initial state of the accumulation. pred2 is a predicate taking two args, the accumulation state and the new input. The process stops when pred2 returns false. ;; distinct (into [] (take-while-accumulating conj #{} (complement contains?)) '(1 2 3 4 2 5 6)) ;;= [1 2 3 4] ;; dedupe (into [] (take-while-accumulating (fn [r x] x) ::void not=) '(1 2 1 3 4 4 5 6)) ;;= [1 2 1 3 4] ;; monotonically increasing (into [] (take-while-accumulating max 0 =) '(1 2 3 4 4 1 5 6)) [1 2 3 4 4] Steve Miner steve...@gmail.com javascript: On May 9, 2015, at 6:28 PM, Andy- andre...@gmail.com javascript: wrote: (defn take-while-xf Takes a transducer and returns a transducer that will immediately finish (ie call (reduced)) when the transducer did not call the reducing function and just returned the result. Only really useful with stateful transducers. Otherwise you'd use take-while. [xf] -- You received this message because you are subscribed to the Google Groups Clojure group. To post to this group, send email to clojure@googlegroups.com Note that posts from new members are moderated - please be patient with your first post. To unsubscribe from this group, send email to clojure+unsubscr...@googlegroups.com For more options, visit this group at http://groups.google.com/group/clojure?hl=en --- You received this message because you are subscribed to the Google Groups Clojure group. To unsubscribe from this group and stop receiving emails from it, send an email to clojure+unsubscr...@googlegroups.com. For more options, visit https://groups.google.com/d/optout.
Re: Opinion on take-while for stateful transducers
Not an expert, but I’ll throw out an alternative approach that might work for you. I think it’s simpler to use a transducer that calls functions rather than trying to transform an existing transducer to do the cutoff. (defn take-while-accumulating [accf init pred2] (fn [rf] (let [vstate (volatile! init)] (fn ([] (rf)) ([result] (rf result)) ([result input] (if (pred2 @vstate input) (do (vswap! vstate accf input) (rf result input)) (reduced result))) accf is like a reducing function: takes two args, state and input, and returns new state of the “accumulation”. init is the initial state of the accumulation. pred2 is a predicate taking two args, the accumulation state and the new input. The process stops when pred2 returns false. ;; distinct (into [] (take-while-accumulating conj #{} (complement contains?)) '(1 2 3 4 2 5 6)) ;;= [1 2 3 4] ;; dedupe (into [] (take-while-accumulating (fn [r x] x) ::void not=) '(1 2 1 3 4 4 5 6)) ;;= [1 2 1 3 4] ;; monotonically increasing (into [] (take-while-accumulating max 0 =) '(1 2 3 4 4 1 5 6)) [1 2 3 4 4] Steve Miner stevemi...@gmail.com On May 9, 2015, at 6:28 PM, Andy- andre.r...@gmail.com wrote: (defn take-while-xf Takes a transducer and returns a transducer that will immediately finish (ie call (reduced)) when the transducer did not call the reducing function and just returned the result. Only really useful with stateful transducers. Otherwise you'd use take-while. [xf] -- You received this message because you are subscribed to the Google Groups Clojure group. To post to this group, send email to clojure@googlegroups.com Note that posts from new members are moderated - please be patient with your first post. To unsubscribe from this group, send email to clojure+unsubscr...@googlegroups.com For more options, visit this group at http://groups.google.com/group/clojure?hl=en --- You received this message because you are subscribed to the Google Groups Clojure group. To unsubscribe from this group and stop receiving emails from it, send an email to clojure+unsubscr...@googlegroups.com. For more options, visit https://groups.google.com/d/optout.
Opinion on take-while for stateful transducers
Hi *, I first needed a transducer that stopped when the first non-distinct item is seen. Thus changing distinct slightly to: (defn take-while-distinct Just like clojure.core/distinct but terminating as soon as one duplicate element is seen. [] (fn [rf] (let [seen (volatile! #{})] (fn ([] (rf)) ;; init arity ([result] (rf result)) ;; completion arity ([result input];; reduction arity (if (contains? @seen input) (reduced result);; !! difference to distinct (do (vswap! seen conj input) (rf result input I figured this can be generalized to also work with dedup and possibly other stateful transducers (after all if they aren't stateful then I can just use take-while) so that I can do something like this: (into [] (take-while-xf (distinct)) [1 2 3,,, 1 4 32 42]) (into [] (take-while-xf (dedupe)) [1 2 3 1 4 32,,, 32 42 292 23]) The comma marks where the transducers stops (after consuming one more input). I came up with this (non-working) implementation: (defn take-while-xf-buggy Takes a transducer and returns a transducer that will immediately finish (ie call (reduced)) when the transducer did not call the reducing function and just returned the result. Only really useful with stateful transducers. [xf] (fn [rf] (let [td (xf rf)] (fn ([] (rf)) ([result] (rf result)) ([result input] (let [x (td result input)] (if (= x result) (reduced x) x))) This doesn't work. I believe because the reducing function bashes the transient in place and the comparison always yields true and thus stops after one input element. I then ended up with something hacky that does seem to work however: (defn take-while-xf Takes a transducer and returns a transducer that will immediately finish (ie call (reduced)) when the transducer did not call the reducing function and just returned the result. Only really useful with stateful transducers. Otherwise you'd use take-while. [xf] (fn [rf] (let [td (xf (fn ;; custom reducing function to avoid calls to rf ([] []) ([x] [x]) ([r i] i)))] (fn ([] (rf)) ([result] (rf result)) ([result input] (let [x (td result input)] (if (= x input) (rf result input) (reduced result ;; or equivalently:(defn take-while-xf' [xf] (let [td (xf (fn ([] []) ([x] x) ([r i] i)))] (take-while #(= % (td nil %) Is this a good idea and rock solid implementation? I'm a little worried this breaks down for corner cases (and this would be no fun to debug). Or am abusing transducers here? What would be a good way to implement this? Possibly even further up the chain? Thanks for reading, Andy -- You received this message because you are subscribed to the Google Groups Clojure group. To post to this group, send email to clojure@googlegroups.com Note that posts from new members are moderated - please be patient with your first post. To unsubscribe from this group, send email to clojure+unsubscr...@googlegroups.com For more options, visit this group at http://groups.google.com/group/clojure?hl=en --- You received this message because you are subscribed to the Google Groups Clojure group. To unsubscribe from this group and stop receiving emails from it, send an email to clojure+unsubscr...@googlegroups.com. For more options, visit https://groups.google.com/d/optout.