Wow, thank you very much! A perfect solution. At the end, wouldn't be good if the reducers would implement alongside CollReduce also a Seqable interface, so that reducers could be used as a drop in replacement for today's sequence functions (map, filter, ...)? CollReduce implements 'eager' computations (when calling reduce and fold) while the Seqable would implement a lazy ones (when calling seq).
Functions working with seqs (e.g. first, rest) always call seq before working with the collection, so this would be transparent for them. It may be a breaking change and complects things a bit though... JW On Thursday, August 8, 2013 3:34:31 PM UTC+2, Christophe Grand wrote: > > ArrayDeque based versions: > > (defn drop-last [n coll] > (reducer coll > (fn [f1] > (let [buffer (java.util.ArrayDeque. (int n))] > (fn self > ([] (f1)) > ([ret x] > (.add buffer x) > (if (<= (count buffer) n) > ret > (f1 ret (.pop buffer))))))))) > > (defn take-last [n coll] > (reify clojure.core.protocols.CollReduce > (coll-reduce [this f1] > (clojure.core.protocols/coll-reduce this f1 (f1))) > (coll-reduce [_ f1 init] > (clojure.core.protocols/coll-reduce > (doto (clojure.core.protocols/coll-reduce > coll > (fn [^java.util.Deque q x] > (.add q x) > (when (> (count q) n) > (.pop q)) > q) (java.util.ArrayDeque. (int n))) prn) > f1 init)))) > > > > On Thu, Aug 8, 2013 at 3:16 PM, Christophe Grand > <chris...@cgrand.net<javascript:> > > wrote: > >> You need to use a buffer to defer calls to the reduced function >> >> (defn drop-last [n coll] >> (reducer coll >> (fn [f1] >> (let [buffer >> (atom clojure.lang.PersistentQueue/EMPTY)] >> (fn self >> ([] (f1)) >> ([ret x] >> (let [b (swap! buffer conj x)] >> (if (<= (count @buffer) n) >> ret >> (do >> (swap! buffer pop) >> (f1 ret (peek b))))))))))) >> >> An array or a ring buffer should be used instead of the atom and >> persistent queue combo to reduce allocation. >> >> take-last is harder because you can't know when the reduction is over >> when using #'reducer, so you have to implement CollReduce yourself: >> >> (defn take-last [n coll] >> (reify clojure.core.protocols.CollReduce >> (coll-reduce [this f1] >> (clojure.core.protocols/coll-reduce this f1 (f1))) >> (coll-reduce [_ f1 init] >> (clojure.core.protocols/coll-reduce >> (clojure.core.protocols/coll-reduce >> coll >> (fn [q x] >> (let [q (conj q x)] >> (if (<= (count q) n) >> q >> (pop q)))) clojure.lang.PersistentQueue/EMPTY) >> f1 init)))) >> >> again, use of a mutable array/buffer would be preferable. >> >> hth, >> >> Christophe >> >> On Thu, Aug 8, 2013 at 1:00 PM, Jozef Wagner >> <jozef....@gmail.com<javascript:> >> > wrote: >> >>> Is it possible to implement efficient butlast (and drop-last, take-last) >>> with reducers? The only solution I can think of needs additional reduce to >>> compute count, which may often be undesirable. >>> >>> Or is it OK to say that reducers are not designed for such cases? >>> >>> JW >>> >>> -- >>> -- >>> You received this message because you are subscribed to the Google >>> Groups "Clojure" group. >>> To post to this group, send email to clo...@googlegroups.com<javascript:> >>> Note that posts from new members are moderated - please be patient with >>> your first post. >>> To unsubscribe from this group, send email to >>> clojure+u...@googlegroups.com <javascript:> >>> 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+u...@googlegroups.com <javascript:>. >>> For more options, visit https://groups.google.com/groups/opt_out. >>> >>> >>> >> >> >> >> -- >> On Clojure http://clj-me.cgrand.net/ >> Clojure Programming http://clojurebook.com >> Training, Consulting & Contracting http://lambdanext.eu/ >> > > > > -- > On Clojure http://clj-me.cgrand.net/ > Clojure Programming http://clojurebook.com > Training, Consulting & Contracting http://lambdanext.eu/ > -- -- 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/groups/opt_out.