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.

Reply via email to