Clojure Golf episode 1
This is an attempt to have some fun with Clojure. I hope to engage the Clojure community, but it would be all the more fun if others played along with their chosen language(s). The idea is that I present a sub-optimal code snippet and people throw in their optimized, smallest, and/or weirdest submissions of that same function.
Episode 1 is the function filter-collecting
taken directly from Greg’s Little Object System — the code is thus:
(defn filter-collecting [predicate collector & lists] (lazy-seq (loop [lists lists out []] (if (empty? (first lists)) (reverse out) (let [heads (map first lists)] (if (apply predicate heads) (recur (map rest lists) (cons (apply collector heads) out)) (recur (map rest lists) out))))))) (filter-collecting (fn [x y] (< x y)) (fn [x y] (+ x y)) ‘(1 7 3 9) ‘(5 5 5 5)) ;;=> (6 8)
In a nutshell, it takes n number of lists
and applies a predicate
to subsequent elements. If said predicate passes, then it applies the collector
function (arity n) and returns that result in a collection. As a starting point, it should be noted that this is a direct translation from Scheme and should be more Clojure-esque
Have fun.
Follow-up
There were some nice follow-ups on the Clojure group (code changes only done for formatting purposes):
Jarkko Oranen
(defn filter-collecting [c f & seqs]
(remove nil?
(apply map (fn [& args]
(when (apply c args)
(apply f args)))
seqs)))
Christophe Grand
my personal favorite
(defn filter-collecting [predicate collector & lists]
(for [v (apply map vector lists)
:when (apply predicate v)]
(apply collector v)))
Chris Houser
(defn filter-collecting [p c & l]
(map #(apply c %)
(filter #(apply p %)
(apply map vector l))))
Laurent Petit
(let [x (Object.)]
(defn filter-collecting [p c & l]
(remove #(identical? % x)
(apply map #(if (apply p %&) (apply c %&) x) l))))
;; AND
(defn filter-collecting [p c & l]
(apply mapcat
#(when (apply p %&) [(apply c %&)]) l))
Jonathan Smith
(defn filter-collecting [p c & seqs]
(let [fun #(if (apply p %1)
(conj! %2 (apply c %1))
%2)]
(loop [hs (map first seqs)
ts (map rest seqs)
result (transient [])]
(if (next (first ts))
(recur (map first ts)
(map rest ts)
(fun hs result))
(persistent! (fun hs result))))))
Thanks to everyone who participated.
-m
6 Comments, Comment or Ping
Ronen
Well iv made another solution (http://snipplr.com/view/18345/my-filter-collection-solution/)
My comments on the original solution:
Thanks for the challenge!
Aug 15th, 2009
Eric Normand
It’s a pretty simple function.
http://gist.github.com/169171
Aug 17th, 2009
fogus
@Ronen,
Thanks for the snippet(s). I tend to like your second solution the best for the same reason that I like Grand’s — it tells the story of its functionality. The next step would be to lose your heads. ;) I agree with all of your comments and it goes to show that although Clojure can quite easily be translated directly from Scheme, that approach is not likely to yield idiomatic results.
-m
Aug 24th, 2009
Saptarshi
Rather later in the day, but for the R language:
filter.collecting <- function(predicate, collector, …){ w <- which(mapply(predicate, …)) newl <- lapply(list(…),function(r) r[w]) newl$FUN <- collector do.call(“mapply”,newl) }
usage ::
filter.collecting(“<“,”+”, c(1,7,3,9),c(5,5,5,5))
Mar 24th, 2010
Saptarshi
Oops, the newlines disappeared, here it is again filter.collecting <- function(predicate, collector, …){
w <- which(mapply(predicate, …))
newl <- lapply(list(…),function(r) r[w])
newl$FUN <- collector
do.call(“mapply”,newl)
}
usage ::
filter.collecting(“<“,”+”, c(1,7,3,9),c(5,5,5,5))
Mar 24th, 2010
Saptarshi
Hello, I apologize for the repeated posts, this the last one for sure. A bit more space efficient:
filter.collecting <- function(predicate, collector,…){
z <- mapply(function(…) if(do.call(predicate,list(…))) do.call(collector,list(…)) ,…,SIMPLIFY=TRUE);
Filter(function(r) !is.null(r), z);
}
filter.collecting(‘<‘,’+’, c(1,7,3,9),c(5,5,5,5));
Mar 24th, 2010
Reply to “Clojure Golf episode 1”