user477768
user477768

Reputation:

where is the bug in this Clojure code?

I have this Clojure code:

(defn apply-all-to-arg [& s]
    (let [arg (first s)
          exprs (rest s)]
        (for [condition exprs] (condition arg))))

(defn true-to-all? [& s]
    (every? true? (apply-all-to-arg s)))

This is test code:

(apply-all-to-arg 2 integer? odd? even?)

=> (true false true)

(every? true? (apply-all-to-arg 2 integer? odd? even?)

=> false

(true-to-all? 2 integer? odd? even?)

=> true

My question is: Why does the function true-to-all? return true (it must have returned false instead)

Upvotes: 3

Views: 210

Answers (4)

NielsK
NielsK

Reputation: 6956

The default Clojure function that creates a function that applies several functions to one argument in parallel is juxt:

=> ((juxt integer? odd? even?) 2)
[true false true]

=> (every? true? ((juxt integer? odd? even?) 2))
false

=> (defn true-to-all? [a & fns]
     (every? true? ((apply juxt fns) a)))

=> (true-to-all? 2 integer? odd? even?)
false

If the functions you combine with juxt all take multiple arguments it works as well

=> ((juxt + - / *) 6 3)
[9 3 2 18]

Upvotes: 2

Ankur
Ankur

Reputation: 33637

You can also defined your function like below to make it more clear.

(defn apply-all-to-arg [v & fns] 
       (map #(% v) fns))

As this makes the function definition clear that it takes a value and optional functions to apply to that value.

Upvotes: 0

sepp2k
sepp2k

Reputation: 370092

true-to-all? calls apply-all-to-arg with the single argument s. So you're not calling (every? true? (apply-all-to-arg 2 integer? odd? even?), but rather:

(every? true? (apply-all-to-arg (list 2 integer? odd? even?))

So in apply-all-to-arg the value of arg will be that list and the value of exprs will be the empty list. Since every? will be true for the empty list no matter what the condition is, you'll get back true.

To fix this you can either change apply-all-to-arg, so that it accepts a list instead of a variable number of arguments, or you can change true-to-all?, so that it passes the contents of s as multiple arguments rather than a single list (by using apply).

Upvotes: 3

Tom
Tom

Reputation: 4782

Because when you call true-to-all?, the parameter s is a list, so you are effectively calling (apply-all-to-arg '(2 integer? odd? even?))

Try defining true-to-all? like this:

(defn true-to-all? [& s]
  (every? true? (apply apply-all-to-arg s))

Upvotes: 0

Related Questions