HB <hubaghd...@gmail.com> writes:

> I'm trying to write a function that calculates the length of a list:
>
> (defn list-length [col]
>   (if col
>     (+ 1 (list-length(rest col)))
>     0))
>
>
> (list-length '(Java, Clojure, Scala))
>
> Upon running it in the REPL, I got the error:
> java.lang.StackOverflowError (test.clj:3)
>
> What is going wrong?

In Clojure (unlike many other lisps) the empty list (), nil and false
are different things.  The empty list is "truthy".  Try this instead:

    (defn list-length [coll]
      (if-let [s (seq coll)]
        (+ 1 (list-length (rest s)))
        0))
    
The seq function will return nil on an empty sequence.  It's also good
form to capture its output as s, so that if you pass in something that's
not a sequence, for example a hash-map, then it doesn't have to be
converted to a seq twice.

The above will still blow up with a StackOverflowError on very large
lists because it is not tail-recursive.  We can transform it into a
tail-recursive form by passing the count-so-far as an optional argument
(initialized to 0) and using recur for the recursive call:

    (defn list-length
      ([coll]   (list-length coll 0))
      ([coll n] (if-let [s (seq coll)]
                  (recur (rest s) (inc n))
                  n)))

That's of course a really common pattern that comes up all the time.  So
Clojure provides a higher-level function (reduce) that does the
iteration and recursion for you.  So a nicer implementation might be:
    
    (defn list-length [coll]
      (reduce (fn [n x] (inc n)) 0 coll))

Of course in practice you should just use the built-in count function
(which is O(1) on most data structures) but I assume this is an
exercise. :-)

If you're interested in the history behind the decision to make the
empty list truthy, have a look here:

    http://clojure.org/lazy

In very early (pre 1.0) versions of Clojure your function would have
actually worked as you expected.  The change from the Common Lisp model
was made to allow for "fully" lazy sequences.

-- 
You received this message because you are subscribed to the Google
Groups "Clojure" group.
To post to this group, send email to clojure@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+unsubscr...@googlegroups.com
For more options, visit this group at
http://groups.google.com/group/clojure?hl=en

Reply via email to