What's the best way to do this?

2 views
Skip to first unread message

nwalex

unread,
Jan 21, 2010, 2:47:01 PM1/21/10
to Clojure
I'm very new to Clojure, and to functional programming in general. Can
anyone tell me the idiomatic way to implement this function?

(defn find-files
"Find files in directory that match predicates pred & others"
[in-dir pred & others]
(filter pred (file-seq in-dir)))

What is the best way to filter a sequence using multiple predicates? I
want to return a sequence containing the files that match predicate
pred and all other predicates bound to 'others'.

Sean Devlin

unread,
Jan 21, 2010, 2:59:24 PM1/21/10
to Clojure
I'm not quite sure what you're getting at. Maybe split-with will do
what you want?

http://richhickey.github.com/clojure/clojure.core-api.html#clojure.core/split-with

Dan Schmidt

unread,
Jan 21, 2010, 3:01:22 PM1/21/10
to clo...@googlegroups.com
I don't know how efficient it is (or how idiomatic it really is), but
this should work:

(defn find-files
"Find files in directory that match predicates pred & others"
[in-dir pred & others]

(reduce (fn [xs f] (filter f xs)) (file-seq in-dir) (cons pred others)))

Dan

> --
> 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
> 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
> For more options, visit this group at
> http://groups.google.com/group/clojure?hl=en
>

nwalex

unread,
Jan 21, 2010, 3:08:07 PM1/21/10
to Clojure
Brilliant. Thanks Dan, that did the trick.

On Jan 21, 8:01 pm, Dan Schmidt <dan.schm...@gmail.com> wrote:
> I don't know how efficient it is (or how idiomatic it really is), but
> this should work:
>
> (defn find-files
>  "Find files in directory that match predicates pred & others"
>  [in-dir pred & others]
>  (reduce (fn [xs f] (filter f xs)) (file-seq in-dir) (cons pred others)))
>
> Dan
>

Perry Trolard

unread,
Jan 21, 2010, 3:40:32 PM1/21/10
to Clojure
I think it's easier to think about combining predicates separately
from your file-filtering code. I'd use a higher-order function like
the following

(defn combine-preds
[& preds]
(fn [& args] (every? #(apply % args) preds)))

then filter files like

(filter (combine-preds pred1 pred2) (file-seq in-dir))

Perry

Sean Devlin

unread,
Jan 21, 2010, 3:49:11 PM1/21/10
to Clojure
I agree with Perry. I've found I have to write two versions of
combine-preds often

(defn every-pred?
"Mimics AND"


[& preds]
(fn [& args] (every? #(apply % args) preds)))

(defn any-pred?
"Mimics OR"
[& preds]
(fn [& args] (some #(apply % args) preds)))

They're very handy, though. Makes it easy to create a complex
predicate from simple ones.

Sean

Richard Newman

unread,
Jan 21, 2010, 3:49:11 PM1/21/10
to clo...@googlegroups.com
> I think it's easier to think about combining predicates separately
> from your file-filtering code. I'd use a higher-order function like
> the following
>
> (defn combine-preds
> [& preds]
> (fn [& args] (every? #(apply % args) preds)))

I've noticed that most uses of this kind of thing are for fixed sets
of predicates, and almost always for two, so I'd define:

(defmacro both [p1 p2]
`(fn [& args#]
(and (apply ~p1 args#)
(apply ~p2 args#))))

Which reads nicely:

(filter (both number? even?) (range 1 7))

and is probably more efficient (not that I've tested it).

You can generalize this to `all`, of course.

Sean Devlin

unread,
Jan 21, 2010, 3:53:01 PM1/21/10
to Clojure
Not as a macro I hope. You lose apply.

David Nolen

unread,
Jan 22, 2010, 1:00:33 AM1/22/10
to clo...@googlegroups.com
I agree, one possible improvement would be short circuiting on the first false pred.

(defn combine-preds [pred & others]
  (fn [v]
    (loop [result true pred pred others others]
      (if (not result)
        result
        (if (nil? pred)
          result
          (recur (pred v) (first others) (rest others)))))))

(defn mod2 [n]
  (zero? (mod n 2)))

(defn mod3 [n]
  (zero? (mod n 3)))

((combine-preds mod2 mod3) 6) ; true, checks both
((combine-preds mod2 mod3) 9) ; false, never gets past first pred

Richard Newman

unread,
Jan 22, 2010, 1:26:06 AM1/22/10
to clo...@googlegroups.com
> I agree, one possible improvement would be short circuiting on the
> first false pred.

every? does short-circuit.

user=> (every? #(% 5)
(list
(fn [a] (println "First a: " a) false)
(fn [a] (println "Second a: " a) true)))

First a: 5
false

David Nolen

unread,
Jan 22, 2010, 1:42:20 AM1/22/10
to clo...@googlegroups.com
Oops you're right :) And better written as well :D


--

Neill Alexander

unread,
Jan 22, 2010, 5:00:38 AM1/22/10
to Clojure
Wow, thanks very much guys. It's a really useful learning experience
to read these different solutions. I'm not yet thinking in Clojure,
but this thread will surely take me another step closer.

On Jan 22, 6:42 am, David Nolen <dnolen.li...@gmail.com> wrote:
> Oops you're right :) And better written as well :D
>

> On Fri, Jan 22, 2010 at 1:26 AM, Richard Newman <holyg...@gmail.com> wrote:
> > I agree, one possible improvement would be short circuiting on the first
> >> false pred.
>
> > every? does short-circuit.
>
> > user=> (every? #(% 5)
> >               (list
> >                 (fn [a] (println "First a: " a) false)
> >                 (fn [a] (println "Second a: " a) true)))
>
> > First a:  5
> > false
>
> > --
> > 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
> > 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<clojure%2Bunsu...@googlegroups.com>

Reply all
Reply to author
Forward
0 new messages