Mark Needham

Thoughts on Software Development

Clojure: A first look at recursive functions

with 4 comments

I'm working through Stuart Halloway's 'Programming Clojure' book and I just got to the section where it first mentions recursive functions.

It's a simple function to countdown from a given number to zero and then return that sequence.

This was one of the examples from the book:

(defn countdown [result x] 
  (if (zero? x)
    result
    (recur (conj result x) (dec x))))

That function could then be called like this:

(countdown [] 5)

I wanted to see what the function would look if we didn't have the empty vector as a parameter.

From playing around with F# and Scala my first thought would be to write the function like this:

(defn count-down [from]
  (defn inner-count [so-far x]
    (if (zero? x)
      so-far
      (inner-count (conj so-far x) (dec x))))
  (inner-count [] from))

As the book points out a bit further on, Clojure doesn't perform automatic tail call optimisation so we end up with a stack overflow exception if we run the function with a big enough input value.

Clojure does optimise calls to 'recur' so it makes more sense to use that if we want to avoid that problem.

This is an example which makes use of that:

(defn count-down [from]
  (defn inner-count [so-far x]
    (if (zero? x)
      so-far
      (recur (conj so-far x) (dec x))))
  (inner-count [] from))

Looking through the Clojure mailing list at a similar problem I noticed that one of the suggestions was to arity overload the function to include an accumulator.

(defn count-down
  ([from]
    (count-down [] from))
  ([so-far from]
    (if (zero? from)
      so-far
      (recur (conj so-far from) (dec from)))))

Written this way it feels a little bit like Haskell or Erlang but probably not idiomatic Clojure.

Anyway on the next page Halloway shows a better way to do this with much less code!

(into [] (take 5 (iterate dec 5)))

I noticed that in Scala the idea of using 'take' and 'drop' on streams of values seems to be quite popular so I'm intrigued as to whether I'll find the same thing with Clojure.

Written by Mark Needham

November 17th, 2009 at 11:10 am

Posted in Clojure

Tagged with

4 Responses to 'Clojure: A first look at recursive functions'

Subscribe to comments with RSS or TrackBack to 'Clojure: A first look at recursive functions'.

  1. Your last example using the arity overload is actually the most idiomatic. Nesting a named function using defn is not idiomatic at all and is discouraged since it is not really defined in the local scope as you may expect, you could use letfn to achieve the same thing. Otherwise you could also use loop as a recur target which works quite nicely, so it may look like this:

    (defn count-down [from]
    (loop [so-far [] down-from from]
    (if (zero? down-from)
    so-far
    (recur (conj so-far down-from) (dec down-from)))))

    Cheers,
    James

    James Sofra

    17 Nov 09 at 12:49 pm

  2. Thanks, I didn't know about defn not being defined in the local scope so I won't be doing that anymore!

    Mark Needham

    18 Nov 09 at 12:09 am

  3. [...] This post was mentioned on Twitter by Mark Needham, ajlopez. ajlopez said: RT @markhneedham: first thoughts on clojure recur – http://bit.ly/3O2lRZ [...]

  4. Very interesting. A simpler version can be seen here.

    http://blog.uk-webdeveloper.co.uk/web-development/what-are-recursive-functions/

    Jez D

    25 Mar 10 at 9:42 am

Leave a Reply