22

How do I do recursion in an anonymous function, without using tail recursion?

For example (from Vanderhart 2010, p 38):

(defn power
  [number exponent]
  (if (zero? exponent)
    1
    (* number (power number (- exponent 1)))))

Let's say I wanted to do this as an anonymous function. And for some reason I didn't want to use tail recursion. How would I do it? For example:

( (fn [number exponent] ......))))) 5 3)
125

Can I use loop for this, or can loop only be used with recur?

Óscar López
  • 232,561
  • 37
  • 312
  • 386
Sonia Hamilton
  • 4,229
  • 5
  • 35
  • 50

5 Answers5

49

The fn special form gives you the option to provide a name that can be used internally for recursion.

(doc fn)
;=> (fn name? [params*] exprs*)

So, add "power" as the name to complete your example.

(fn power [n e]
  (if (zero? e)
    1
    (* n (power n (dec e)))))

Even if the recursion happened in the tail position, it will not be optimized to replace the current stack frame. Clojure enforces you to be explicit about it with loop/recur and trampoline.

Jeremy
  • 22,188
  • 4
  • 68
  • 81
  • 1
    Thanks Jeremy, I didn't know about the name option. I'm working through the [4clojure](http://www.4clojure.com/) questions and they don't allow defn. Tail recursion is obviously better, but I want to walk before I run :) – Sonia Hamilton May 08 '12 at 06:40
18

I know that in Clojure there's syntactic support for "naming" an anonymous function, as other answers have pointed out. However, I want to show a first-principles approach to solve the question, one that does not depend on the existence of special syntax on the programming language and that would work on any language with first-order procedures (lambdas).

In principle, if you want to do a recursive function call, you need to refer to the name of the function so "anonymous" (i.e. nameless functions) can not be used for performing a recursion ... unless you use the Y-Combinator. Here's an explanation of how it works in Clojure.

Let me show you how it's used with an example. First, a Y-Combinator that works for functions with a variable number of arguments:

(defn Y [f]
  ((fn [x] (x x))
   (fn [x]
       (f (fn [& args]
              (apply (x x) args))))))

Now, the anonymous function that implements the power procedure as defined in the question. Clearly, it doesn't have a name, power is only a parameter to the outermost function:

(fn [power]
      (fn [number exponent]
          (if (zero? exponent)
              1
              (* number (power number (- exponent 1))))))

Finally, here's how to apply the Y-Combinator to the anonymous power procedure, passing as parameters number=5 and exponent=3 (it's not tail-recursive BTW):

((Y 
  (fn [power]
      (fn [number exponent]
          (if (zero? exponent)
              1
              (* number (power number (- exponent 1)))))))
 5 3)

> 125
Óscar López
  • 232,561
  • 37
  • 312
  • 386
  • 3
    Gracias Óscar. The Y-Combinator looks very interesting - I'm going to study it more! – Sonia Hamilton May 08 '12 at 06:44
  • Another good source for understanding the Y combinator is [The Little Schemer](https://mitpress.mit.edu/books/little-schemer). – Mars Apr 30 '16 at 14:17
4

fn takes an optional name argument that can be used to call the function recursively.

E.g.:

user> ((fn fact [x]
           (if (= x 0)
               1
               (* x (fact (dec x)))))
       5)
;; ==> 120
gregspurrier
  • 1,448
  • 2
  • 13
  • 13
3

Yes you can use loop for this. recur works in both loops and fns

user> (loop [result 5 x 1] (if (= x 3) result (recur (* result 5) (inc x))))
125

an idomatic clojure solution looks like this:

user> (reduce * (take 3 (repeat 5)))
125

or uses Math.pow() ;-)

user> (java.lang.Math/pow 5 3)
125.0
Arthur Ulfeldt
  • 90,827
  • 27
  • 201
  • 284
0

loop can be a recur target, so you could do it with that too.

BillRobertson42
  • 12,602
  • 4
  • 40
  • 57