Presentation is loading. Please wait.

Presentation is loading. Please wait.

Clojure Template Tail Recursion. Hello, Factorial! The factorial function is everybody’s introduction to recursion (defn factorial-1 [n] (if (zero? n)

Similar presentations


Presentation on theme: "Clojure Template Tail Recursion. Hello, Factorial! The factorial function is everybody’s introduction to recursion (defn factorial-1 [n] (if (zero? n)"— Presentation transcript:

1 Clojure Template Tail Recursion

2 Hello, Factorial! 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 Not a problem if the number of recurrences is small 2

3 Tail recursion, or tail call recursion A function is tail recursive if, for every recursive call in the function, the recursive call is the last thing done in the function 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 To make the factorial function tail recursive, we need to somehow bring the multiplication into the parameter list 3

4 Adding an accumulator (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

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

6 Use of a fa ҫ ade 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)))) This adds an unnecessary function to those available to the user 6

7 When tail recursion isn’t 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

8 Polymorphic parameters 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

9 Defining a local helper function (defn factorial-4 [number] (let [factorial-helper (fn [acc n] (if (zero? n) acc (recur (* acc n) (dec n))))] (factorial-helper 1 number))) 9

10 let and loop 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 [ name1 value1, …, nameN valueN ] 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 [ name1 value1, …, nameN valueN ] code ) 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

11 let and loop comparison (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

12 shallow-reverse (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

13 find-first-index 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

14 Find the average of a sequence (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

15 Summary: Tail recursion 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 15 …and don’t fortet to use recur !

16 16 The End


Download ppt "Clojure Template Tail Recursion. Hello, Factorial! The factorial function is everybody’s introduction to recursion (defn factorial-1 [n] (if (zero? n)"

Similar presentations


Ads by Google