cs 251 fall 2019 cs 251 fall 2019 topics principles of
play

CS 251 Fall 2019 CS 251 Fall 2019 Topics Principles of - PowerPoint PPT Presentation

CS 251 Fall 2019 CS 251 Fall 2019 Topics Principles of Programming Languages Principles of Programming Languages Ben Wood Ben Wood Recursion is an elegant and natural match for many computations and data structures. Tail Recursion


  1. λ λ CS 251 Fall 2019 CS 251 Fall 2019 Topics Principles of Programming Languages Principles of Programming Languages Ben Wood Ben Wood Recursion is an elegant and natural match for many computations and data structures. Tail Recursion • Natural recursion with immutable data can be space- inefficient compared to loop iteration with mutable data. • Tail recursion eliminates the space inefficiency with a Ta simple, general pattern. • Recursion over immutable data expresses iteration more clearly than loop iteration with mutable state. • More higher-order patterns: fold https://cs.wellesley.edu/~cs251/f19/ 1 Tail Recursion 2 Tail Recursion Naturally recursive factorial CS 240-style machine model Code Co Stack St Re Registers Call frame (define (fact n) fixed size, general purpose (if (= n 0) 1 Call frame (* n (fact (- n 1))))) Call frame Heap Hea arguments, variables, return address per function call Sp Space: e: O( ) Program How efficient is this implementation? Counter cons cells, data structures, … Time: O( ) Ti Stack Pointer Tail Recursion 3 Tail Recursion 4

  2. Example Naturally recursive factorial (define (fact n) (if (= n 0) 1 (* n (fact (- n 1))))) (define (fact n) (fact 3) (fact 3): 3*_ (fact 3): 3*_ (fact 3): 3*_ Compute result so far (fact 2) (fact 2): 2*_ (fact 2): 2*_ (if (= n 0) af after/from recursive call. 1 Base case returns (fact 1) (fact 1): 1*_ base result. (* n (fact (- n 1))))) Remember : n ↦ 2 ; and (fact 0) Recursive case returns “rest of function” for this call. Compute remaining argument result so far. before/for recursive call. be (fact 3): 3*_ (fact 3): 3*_ (fact 3): 3*_ (fact 3): 3*2 (fact 2): 2*_ (fact 2): 2*_ (fact 2): 2*1 Space: Sp e: O( ) (fact 1): 1*_ (fact 1): 1*1 Time: O( ) Ti (fact 0): 1 Tail Recursion 5 Tail Recursion 6 Tail recursive factorial Common patterns of work Natural recursion: Tail recursion: Accumulator parameter provides result so far. (define (fact n) Argument Full result Argument Base result (define (fact-tail n acc) Compute result so far (if (= n 0) before/for recursive call. be acc Deeper recursive calls Base case returns Re Reduce Reduce Re full result. (fact-tail (- n 1) (* n acc)))) argument argument Recursive case returns Ac Accumulate Ac Accumulate Compute remaining argument full result. result result before/for recursive call. be (fact-tail n 1)) so far so far Initial accumulator provides base result. Base case Base result Base case Full result Tail Recursion 7 Tail Recursion 8

  3. ar argume ment fu full ll result lt argume ar ment ba base result Natural Tail (fact 4): 24 (fact-tail 4, 1): 24 recursion recursion -1 -1 * * accumulate accumulate reduce (fact 3): 6 reduce (fact-tail 3, 4): 24 -1 -1 * * Recursive case: Recursive case: Compute result Compute recursive argument (fact 2): 2 (fact-tail 2, 12): 24 in terms of argument and in terms of argument and accumulated recursive result. -1 accumulator. -1 * * (fact 1): 1 (fact-tail 1, 24): 24 -1 -1 * * (fact 0): 1 (fact-tail 0, 24): 24 (define (fact n) (define (fact n) base case ba ba base result base case ba fu full ll result lt (define (fact-tail n acc) (if (= n 0) (if (= n 0) 1 acc (* n (fact (- n 1))))) (fact-tail (- n 1) (* n acc)))) (fact-tail n 1)) Tail Recursion 9 Tail Recursion 10 The call stacks Optimization under the hood Nothing useful remembered here. Space: Sp e: O( ) (define (fact n) (fact 3) (fact 3): _ (fact 3): _ (fact 3): _ (define (fact-tail n acc) (ft 3 1) (ft 3 1):_ (ft 3 1):_ (if (= n 0) Ti Time: O( ) acc (ft 2 3) (ft 2 3):_ ft = fact-tail (fact-tail (- n 1) (* n acc)))) (fact-tail n 1)) (ft 1 6) (fact 3) (ft 3 1) (ft 2 3) (ft 1 6) (ft 0 6) (fact 3): _ (fact 3): _ (fact 3): _ (fact 3): _ Language implementation recognizes tail calls. (ft 3 1):_ (ft 3 1):_ (ft 3 1):_ (ft 3 1):_ • Caller frame never needed again. (ft 2 3):_ (ft 2 3):_ (ft 2 3):_ (ft 2 3):6 • Reuse same space for every recursive tail call. • Low-level: acts just like a loop. (ft 1 6):_ (ft 1 6):_ (ft 6 1):6 etc. (ft 0 6) (ft 0 6):6 Racket, ML, most “functional” languages, but not Java, C, etc. Tail Recursion 11 Tail Recursion 12

  4. Tail recursion transformation Example (define (sum xs) (define (fact n) natur na ural rec ecur ursion (if (null? xs) (if (= n 0) 0 1 (+ (car xs) (sum (cdr xs))))) (* n (fact (- n 1)) ) )) ta tail il r recursio ion (define (fact n) (define (fact-tail n acc ) (define (sum xs) (if (= n 0) (define (sum-tail xs acc) Ac Accumulator acc (if (null? xs) be becomes (fact-tail (- n 1) (* n acc ) ))) ba base result. acc (fact-tail n 1 )) (sum-tail (cdr xs) (+ (car xs) acc))) (sum-tail xs 0)) Re Recursive ve step applied to ac accumulat ator in instead d of re recursive re result. Base resul Bas ult becomes init in itia ial l accumula lator. Tail Recursion 13 Tail Recursion 14 Tail call intuition: “nothing left for caller to do”, Practice Tail position “callee result is immediate caller result” (define (rev xs) Recursive definition of ta tail il p positio ition: – In (lambda (x1 … xn) e ) , the body e is in tail position. – If If (if e1 e2 e3 ) is is in in tail il posit itio ion , then e2 and e3 are in tail position (but e1 is not). (define (rev xs) – If If (let ([x1 e1] … [xn en]) e ) is is in in tail il posit itio ion , then e is in tail position (but the binding expressions are not). Note: – If a non-lambda expression is not in tail position, then no • Naturally recursive rev is O(n 2 ): each recursive call must traverse to subexpressions are. end of list and build a fully new list. – Critically, in a function call expression (e1 e2) , subexpressions e1 – 1+2+…+(n-1) is almost n*n/2 and e2 are no not in tail position. – Moral: beware append, especially within outer recursion • Tail-recursive rev is O(n). What about map, filter? – Cons is O(1), done n times. A ta tail call is a function call in tail position. Tail Recursion 15 Tail Recursion 16

  5. Why tail recursion instead of Identify dependences between ________. loops with mutation? Ra Racket: immutable le natural l recursion (define (fib n) re recursiv ive (if (< n 2) calls alls 1. Simpler language, but just as efficient. n (+ (fib (- n 1)) (fib (- n 2))))) 2. Explicit dependences for easier reasoning. Ra Racket: immutable le tail l recursion (define (fib n) – Especially with HOFs like fold! (define (fib-tail n fibi fibi+1) (if (= 0 n) fibi (fib-tail (- n 1) fibi+1 (+ fibi fibi+1)))) (fib n 0 1)) def fib(n): Python: lo loop iterat ation with mutat ation fib_i = 0 fib_i_plus_1 = 1 lo loop for i in range(n): fib_i_prev = fib_i iterat it ratio ions fib_i = fib_i_plus_1 fib_i_plus_1 = fib_i_prev + fib_i_plus_1 return fib_i Tail Recursion 17 Tail Recursion 18 What must we inspect to Wh Identify dependences between ________. HO HOF HO HOF Fold: iterator over recursive structures Fo Ra Racket: immutable le natural l recursion (define (fib n) re recursiv ive (a.k.a. reduce , inject , …) (if (< n 2) calls alls n (fold_ combine init list) (+ (fib (- n 1)) (fib (- n 2))))) accumulates result by iteratively applying Ra Racket: immutable le tail l recursion (define (fib n) (combine element accumulator) (define (fib-tail n fibi fibi+1) to each element of the list and accumulator so far (if (= 0 n) (starting from init ) to produce the next accumulator . fibi (fib-tail (- n 1) fibi+1 (+ fibi fibi+1)))) (fib n 0 1)) – (foldr f init (list 1 2 3)) def fib(n): Python: lo loop iterat ation with mutat ation computes (f 1 (f 2 (f 3 init))) fib_i = 0 fib_i_plus_1 = 1 lo loop for i in range(n): – (foldl f init (list 1 2 3)) fib_i_prev = fib_i it iterat ratio ions computes (f 3 (f 2 (f 1 init))) fib_i = fib_i_plus_1 fib_i_plus_1 = fib_i_prev + fib_i_plus_1 return fib_i Tail Recursion 19 Tail Recursion 20

  6. Folding geometry Super-iterators! Na Natural recursio ion • Not built into the language (foldr combine init L) – Just a programming pattern – Many languages have built-in support, often allow stopping early ⋯ without resorting to exceptions re result combine co ne combine co ne co combine ne combine co ne in init it • Pattern separates recursive traversal from data processing ⋯ L ⟼ – Reuse same traversal, different folding functions V V 2 V n-1 V n 1 – Reuse same folding functions, different data structures – Common vocabulary concisely communicates intent ⋯ co combine ne co combine ne co combine ne co combine ne re result init in it • map , filter , fold + cl closures/lexica cal sco cope = superpower – Next: argument function can use any “private” data in its environment. (foldl combine init L) – Iterator does not have to know or help. Tail recursion Ta Tail Recursion 21 Tail Recursion 22

Recommend


More recommend