Reputation: 585
Why those two functions have a different return?
(filter (and #(= (rem % 2) 1)
#(= (rem % 3) 0))
(take 100 (iterate inc 0))) ;=> (0 3 6 9 12...
(filter #(and (= (rem % 2) 1)
(= (rem % 3) 0))
(take 100 (iterate inc 0))) ;=> (3 9 15 21...
Is the form with two anonymous functions valid?
Upvotes: 2
Views: 114
Reputation: 29958
Expanding on the other answer...
You have accidentally discovered a "feature" of Clojure which, IMHO, causes more trouble than it is worth.
Consider what Clojure considers "truthy":
(defn truthy?
[x]
(if x
:yes
:no))
(dotest
(is= :yes (truthy? true))
(is= :yes (truthy? 1))
(is= :yes (truthy? :a))
(is= :no (truthy? nil))
(is= :no (truthy? false)))
Note that a function object is also "truthy":
(dotest
(let [fn-obj-1 (fn [x] (+ x 1))
fn-obj-2 #(+ % 1)] ; shorthand, equiv to fn-obj-1
(is= :yes (truthy? fn-obj-1))
(is= :yes (truthy? fn-obj-2))
(is= 42 (fn-obj-1 41)) ; call the fn
(is= 42 (fn-obj-2 41)) ; call the fn
))
and
macroThe and
macro (source code) doesn't just return a boolean value as you may expect. Instead, it returns the item that "caused" the result:
(dotest
(is= 3 (and 1 2 3)) ; 1
(is= :a (and 99 :a)) ; 2
(is= nil (and nil :a)) ; 3
(is= false (and false 88))) ; 4
So if all the items are "truthy", and
returns the last one since it is the "deciding factor" (cases 1 & 2). If one or more "falsey" values are present, and
returns the first one found since it is the "deciding factor" (cases 3 & 4).
Personally, I never use this feature of Clojure. IMHO it is something of a "trick" that makes code difficult to decipher correctly.
and
macro work?In order to return the "deciding factor" to you, the and
macro expands into code like this:
; (and 99 :a)
(let [x 99]
(if x
; recursive invocation: (and :a)
x))
So if the 99
had been false or nil instead, it would have been returned to you. Since 99
is truthy, and
recurses with the next value :a
as follows:
; (and :a)
:a
Since there is only 1 item in this recursion level, it must be the "deciding factor" and the and
macro just returns the raw value to you.
and
is a macroThis means it runs at compile time (every macro is properly regarded as a "compiler extension"). In your first case, your code looks like so:
(filter (and f1 f2) ...)
where f1
and f2
are both functions. Since both are "truthy?", and
returns the last item to you. Since the above code rewriting with let
etc occurs at compile time, your code gets converted into:
(filter
(let [x f1]
(if x
f2
f1))
...)
And since f1
is truthy, that simplifies to
(filter
f2
...)
where f2
is just #(= (rem % 3) 0)
. So we see [0 3 6 ...] returned.
Because of a Clojure "quirk", your first filter
does not generate a compiler error (although I think it should). The 2nd form does what you want, since filter
expects exactly 1 function to use in deciding which items are retained.
The above is based on this template project.
Upvotes: 3
Reputation: 3528
The expression
(and #(= (rem % 2) 1) #(= (rem % 3) 0))
is the same as just writing the following:
#(= (rem % 3) 0))
This is because and
returns the first parameter that is false
or nil
. If there are none, it returns the last parameter.
If you want to combine two functions, you can use every-pred:
(every-pred #(= (rem % 2) 1) #(= (rem % 3) 0))
Upvotes: 3