Clojure Template Tail Recursion Hello Factorial n n



![Adding an accumulator n n (defn factorial-two-args [acc n] (if (zero? n) acc (recur Adding an accumulator n n (defn factorial-two-args [acc n] (if (zero? n) acc (recur](https://slidetodoc.com/presentation_image_h2/8b2e4b8c2d37eb6d80af39ada378b6f7/image-4.jpg)


![When tail recursion isn’t n n The function (defn factorial-? [acc n] (if (zero? When tail recursion isn’t n n The function (defn factorial-? [acc n] (if (zero?](https://slidetodoc.com/presentation_image_h2/8b2e4b8c2d37eb6d80af39ada378b6f7/image-7.jpg)

![Defining a local helper function n (defn factorial-4 [number] (let [factorial-helper (fn [acc n] Defining a local helper function n (defn factorial-4 [number] (let [factorial-helper (fn [acc n]](https://slidetodoc.com/presentation_image_h2/8b2e4b8c2d37eb6d80af39ada378b6f7/image-9.jpg)

![let and loop comparison n n (defn factorial-4 [number] (let [factorial-helper (fn [acc n] let and loop comparison n n (defn factorial-4 [number] (let [factorial-helper (fn [acc n]](https://slidetodoc.com/presentation_image_h2/8b2e4b8c2d37eb6d80af39ada378b6f7/image-11.jpg)
![shallow-reverse n n (defn shallow-reverse-1 ([lst] (shallow-reverse () lst)) ([acc lst] (if (empty? lst) shallow-reverse n n (defn shallow-reverse-1 ([lst] (shallow-reverse () lst)) ([acc lst] (if (empty? lst)](https://slidetodoc.com/presentation_image_h2/8b2e4b8c2d37eb6d80af39ada378b6f7/image-12.jpg)

![Find the average of a sequence n n (defn avg [a-seq] (loop [sum 0, Find the average of a sequence n n (defn avg [a-seq] (loop [sum 0,](https://slidetodoc.com/presentation_image_h2/8b2e4b8c2d37eb6d80af39ada378b6f7/image-14.jpg)


- Slides: 16

Clojure Template Tail Recursion

Hello, Factorial! n n The factorial function is everybody’s introduction to recursion (defn factorial-1 [n] (if (zero? n) 1 (* n (factorial-1 (dec n))))) (factorial-1 10) ; => 3628800 The problem with this function is that every recurrence increases the stack size n Not a problem if the number of recurrences is small 2

Tail recursion, or tail call recursion n A function is tail recursive if, for every recursive call in the function, the recursive call is the last thing done in the function n In this situation, the compiler can replace the recursion with a simple loop, which does not add frames to the stack The programmer still does not have (or need!) loops In factorial-1, (* n (factorial-1 (dec n))) the multiplication keeps it from being tail recursive n To make the factorial function tail recursive, we need to somehow bring the multiplication into the parameter list 3
![Adding an accumulator n n defn factorialtwoargs acc n if zero n acc recur Adding an accumulator n n (defn factorial-two-args [acc n] (if (zero? n) acc (recur](https://slidetodoc.com/presentation_image_h2/8b2e4b8c2d37eb6d80af39ada378b6f7/image-4.jpg)
Adding an accumulator n n (defn factorial-two-args [acc n] (if (zero? n) acc (recur (* acc n) (dec n)))) One way to think of this is as “pumping” information from the input parameter to the accumulator 4

Using factorial-two-args n n n (factorial-helper 1 10) ; => 3628800 (factorial-helper 0 10) ; => 0 (factorial-helper 10 1) ; => 10 5

Use of a faҫade n n We can use a façade along with the “real” function (now called “factorial-helper” (defn factorial-2 [n] (factorial-helper 1 n)) (defn factorial-helper [acc n] (if (zero? n) acc (recur (* acc n) (dec n)))) n This adds an unnecessary function to those available to the user 6
![When tail recursion isnt n n The function defn factorial acc n if zero When tail recursion isn’t n n The function (defn factorial-? [acc n] (if (zero?](https://slidetodoc.com/presentation_image_h2/8b2e4b8c2d37eb6d80af39ada378b6f7/image-7.jpg)
When tail recursion isn’t n n The function (defn factorial-? [acc n] (if (zero? n) acc (factorial-? (* acc n) (dec n)))) is tail recursive, but that doesn’t do you any good unless you tell the compiler to replace the recursion with a loop Use recur instead of factorial-? in the tail call 7

Polymorphic parameters n n Clojure functions can be defined with more than one parameter list (defn factorial-3 ([n] (factorial-3 1 n)) ([acc n] (if (zero? n) acc (recur (* acc n) (dec n)) ) 8
![Defining a local helper function n defn factorial4 number let factorialhelper fn acc n Defining a local helper function n (defn factorial-4 [number] (let [factorial-helper (fn [acc n]](https://slidetodoc.com/presentation_image_h2/8b2e4b8c2d37eb6d80af39ada378b6f7/image-9.jpg)
Defining a local helper function n (defn factorial-4 [number] (let [factorial-helper (fn [acc n] (if (zero? n) acc (recur (* acc n) (dec n))))] (factorial-helper 1 number))) 9

let and loop n n n In the previous example, we used (let [factorial-helper (fn [acc n] …]…) to define a helper function with local scope The general form is (let [name 1 value 1, …, name. N value. N] code) and we use recur in the code to tell the compiler to turn this into a loop To simplify this, Clojure provides a loop construct: (loop [name 1 value 1, …, name. N value. N] code) n n n This is not a loop; it’s a request to the compiler to turn tail recursion into a loop! The name/value pairs are initial values of parameters The call to recur in the body supplies new values for the parameters 10
![let and loop comparison n n defn factorial4 number let factorialhelper fn acc n let and loop comparison n n (defn factorial-4 [number] (let [factorial-helper (fn [acc n]](https://slidetodoc.com/presentation_image_h2/8b2e4b8c2d37eb6d80af39ada378b6f7/image-11.jpg)
let and loop comparison n n (defn factorial-4 [number] (let [factorial-helper (fn [acc n] (if (zero? n) acc (recur (* acc n) (dec n))))] (factorial-helper 1 number))) (defn factorial-5 [number] (loop [acc 1, n number] (if (zero? n) acc (recur (* acc n) (dec n))))) 11
![shallowreverse n n defn shallowreverse1 lst shallowreverse lst acc lst if empty lst shallow-reverse n n (defn shallow-reverse-1 ([lst] (shallow-reverse () lst)) ([acc lst] (if (empty? lst)](https://slidetodoc.com/presentation_image_h2/8b2e4b8c2d37eb6d80af39ada378b6f7/image-12.jpg)
shallow-reverse n n (defn shallow-reverse-1 ([lst] (shallow-reverse () lst)) ([acc lst] (if (empty? lst) acc (recur (cons (first lst) acc) (rest lst)) ) (defn shallow-reverse-2 [lst] (loop [acc (), lst-2 lst] (if (empty? lst-2) acc (recur (cons (first lst-2) acc) (rest lst-2)) ) 12

find-first-index n n Problem: Find the index of the first thing in a sequence that satisfies a given predicate (defn find-first-index [pred a-seq] (loop [acc 0, b-seq a-seq] (cond (empty? b-seq) nil (pred (first b-seq)) acc : else (recur (inc acc) (rest b-seq)) ) 13
![Find the average of a sequence n n defn avg aseq loop sum 0 Find the average of a sequence n n (defn avg [a-seq] (loop [sum 0,](https://slidetodoc.com/presentation_image_h2/8b2e4b8c2d37eb6d80af39ada378b6f7/image-14.jpg)
Find the average of a sequence n n (defn avg [a-seq] (loop [sum 0, n 0, b-seq a-seq] (if (empty? b-seq) (/ sum n) (recur (+ sum (first b-seq)) (inc n) (rest b-seq)) ) Notice that the loop takes 3 arguments 14

Summary: Tail recursion n Tail recursion saves stack space, but the code is somewhat harder to read When the recursion is guaranteed to be “not too deep, ” you can ignore tail recursion Tail recursion isn’t too hard, as long as you remember the “bucket” analogy …and don’t fortet to use recur! 15

The End 16