There is an interesting way to do it that does rely neither on rebinding nor the behavior of def. The main trick is to go around the limitations of recursion by passing a function as an argument to itself:
(defn make-fibo [y]
(let
[fib
(fn [mem-fib x]
(let [fib (fn [a] (mem-fib mem-fib a))]
(if (<= x 2)
y
(+ (fib (- x 1)) (fib (- x 2))))))
mem-fib (memoize fib)]
(partial mem-fib mem-fib)))
Then:
> ((make-fibo 1) 50)
12586269025
What happens here:
- The
fibrecursive function got a new argumentmem-fib. This will be the memoized version offibitself, once it gets defined. - The
fibbody is wrapped in aletform that redefines calls tofibso that they pass themem-fibdown to next levels of recursion. mem-fibis defined as memoizedfib- … and will be passed by
partialas the first argument to itself to start the above mechanism.
This trick is similar to the one used by the Y combinator to calculate function’s fix point in absence of a built-in recursion mechanism.
Given that def “sees” the symbol being defined, there is little practical reason to go this way, except maybe for creating anonymous in-place recursive memoized functions.