Clojure Template Tail Recursion Hello Factorial n n
- 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 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 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 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 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
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 [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
- Responses to hello
- Clojure recur
- Clojure factorial
- Clojure factorial
- The greetings song
- Hello hello hello what's your name
- Tail recursion vs iteration
- Tail recursive
- Tail recursion
- Quicksort tail recursion
- Direct and indirect recursive algorithm
- To understand recursion you must understand recursion
- Clojure cartesian product
- Clojure language
- Clojure haskell
- Clojure app engine
- Penox mexico