read


read
or learn more

Clojure Golf episode 1

Aug 14, 2009

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 filtercollecting [c f & seqs]
  (remove nil?
          (apply map (fn [& args]
                       (when (apply c args)
                         (apply f args)))
                 seqs)))

Christophe Grand

my personal favorite

(defn filtercollecting [predicate collector & lists]
  (for [v (apply map vector lists)
          :when (apply predicate v)]
    (apply collector v)))

Chris Houser

(defn filtercollecting [p c & l]
  (map #(apply c %)
       (filter #(apply p %)
               (apply map vector l))))

Laurent Petit

(let [x (Object.)]
   (defn filtercollecting [p c & l]
      (remove #(identical? % x)
        (apply map #(if (apply p %&) (apply c %&) x) l))))

;; AND

(defn filtercollecting [p c & l]
  (apply mapcat
         #(when (apply p %&) [(apply c %&)]) l))

Jonathan Smith

(defn filtercollecting [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

  1. Well iv made another solution (http://snipplr.com/view/18345/my-filter-collection-solution/)

    My comments on the original solution:

    • Using multiple if forms feels a bit clunky to me (& not so functional).
    • The single function makes it a little harder to follow.
    • The need to reverse the result feels a bit awkward.

    Thanks for the challenge!

  2. It’s a pretty simple function.

    http://gist.github.com/169171

  3. @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

  4. 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))

  5. 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))

  6. 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));

Reply to “Clojure Golf episode 1”