tags:

views:

85

answers:

3

I would like to get the smallest element from a vector. For this I use combine the reduce and min functions. However, when providing my own implementation of min I get unexpected results:

user=> (reduce (fn [x y] (< x y) x y) [1 2 3 2 1 0 1 2])
2
user=> (reduce min [1 2 3 2 1 0 1 2 3])
0

The reduce with standard min returns 0 as expected. However, when I provide my own implementation it returns 2. What am I doing wrong?

+7  A: 

You're missing an if:

(reduce (fn [x y] (if (< x y) x y)) ...)
                   ^-- note the if

works fine. :-)

Michał Marczyk
A: 

A fn used with reduce probably needs to handle 3 arities - 0, 1, and 2 arguments.

dnolen
Not really. The implementation of `min` provides an example of `reduce` being used with a function which does not have a nullary implementation in Clojure itself. Also, the unary case is never used by Clojure's `reduce`; a sequence of length 1 is always reduced to its only element. Ultimately you need a nullary case if you're going to deal with empty sequences (if you're willing to treat them as erroneous input, you can do without it) and you definitely need a binary case (if you're never going to deal with sequences of length > 1, there's no point to using `reduce` anyway).
Michał Marczyk
@Michal, ah you're totally right. 0 and 2 arguments.
dnolen
+4  A: 

You are missing if around the function's body. What happens now is:

user> (use 'clojure.contrib.trace)
nil
user> (defn foo [x y] (< x y) x y)
#'user/foo
user> (dotrace [foo] (reduce foo [1 2 3 2 1 0 1 2]))
TRACE t2229: (foo 1 2)
TRACE t2229: => 2
TRACE t2230: (foo 2 3)
TRACE t2230: => 3
TRACE t2231: (foo 3 2)
TRACE t2231: => 2
TRACE t2232: (foo 2 1)
TRACE t2232: => 1
TRACE t2233: (foo 1 0)
TRACE t2233: => 0
TRACE t2234: (foo 0 1)
TRACE t2234: => 1
TRACE t2235: (foo 1 2)
TRACE t2235: => 2
2

In other words the function you pass in always returns y, so in the last iteration 2 is returned, since 2 is the last number of the sequence you reduced.

Also note that min already is based on reduce:

(defn min
  "Returns the least of the nums."
  ([x] x)
  ([x y] (if (< x y) x y))
  ([x y & more]
   (reduce min (min x y) more)))
Michiel Borkent