overview
play

Overview What is itera*on? Racket has no loops, and yet can express - PowerPoint PPT Presentation

Overview What is itera*on? Racket has no loops, and yet can express itera*on. Iteration via Tail Recursion in Racket How can that be? - Tail recursion! Tail recursive list processing via foldl Other useful abstrac*ons - General


  1. Overview • What is itera*on? • Racket has no loops, and yet can express itera*on. Iteration via Tail Recursion in Racket How can that be? - Tail recursion! • Tail recursive list processing via foldl • Other useful abstrac*ons - General itera*on via iterate and iterate-apply CS251 Programming Languages - General itera*on via genlist and genlist-apply Fall 2016, Lyn Turbak Department of Computer Science Wellesley College Iteration/Tail Recursion 2 An itera*ve approach to factorial Factorial Revisited Invocation Tree State Variables: Idea: multiply (fact-rec 4): 24 (define (fact-rec n) • num is the current number being processed. on way down (if (= n 0) -1 4 1 • ans is the product of all numbers already processed. 1 * (* n (fact-rec (- n 1))))) (fact-rec 3): 6 -1 * Iteration Table: -1 * step num ans 3 4 pending multiplication 1 4 1 is nontrivial glue step divide glue (fact-rec 2): 2 -1 2 3 4 * divide 3 2 12 -1 * 2 12 4 1 24 (fact-rec 1): 1 5 0 24 -1 * -1 * 1 24 Iteration Rules: (fact-rec 0): 1 • next num is previous num minus 1. -1 * • next ans is previous num times previous ans. Iteration/Tail Recursion 3 Iteration/Tail Recursion 4 24 0

  2. Tail-recursive factorial: invocation tree Itera*ve factorial: tail recursive version ;; Here, and in many tail recursions, need a wrapper Iteration Rules: ;; function to initialize first row of iteration • next num is previous num minus 1. ;; table. E.g., invoke (fact-iter 4) to calculate 4! Invocation Tree: • next ans is previous num times previous ans. (define (fact-iter n) (fact-tail n 1)) (fact-iter 4) (define (fact-tail num ans ) (define (fact-tail num ans) (if (= num 0) (fact-iter 4 1) (if (= num 0) ans ans stopping divide (fact-tail (- num 1) (* num ans)))) (fact-tail (- num 1) (* num ans)))) (fact-iter 3 4) condition Iteration Table: (fact-iter 2 12) step num ans ;; Here, and in many tail recursions, need a wrapper (fact-iter 1 24) 1 4 1 ;; function to initialize first row of iteration 2 3 4 (fact-iter 0 24) ;; table. E.g., invoke (fact-iter 4) to calculate 4! 3 2 12 (define (fact-iter n) no glue! 4 1 24 (fact-tail n 1)) 5 0 24 Iteration/Tail Recursion 5 Iteration/Tail Recursion 6 inc-rec in Racket The essence of itera*on in Racket ; Extremely silly and inefficient recursive incrementing ; function for testing Racket stack memory limits • A process is iterative if it can be expressed as a sequence of (define (inc-rec n) steps that is repeated until some stopping condition is reached. (if (= n 0) • In divide/conquer/glue methodology, an iterative process is a 1 recursive process with a single subproblem and no glue step. (+ 1 (inc-rec (- n 1))))) • Each recursive method call is a tail call -- i.e., a method call with no pending operations after the call. When all recursive calls of a method are tail calls, it is said to be tail recursive. > (inc-rec 1000000) ; 10^6 A tail recursive method is one way to specify an iterative 1000001 process. > (inc-rec 10000000) Iteration is so common that most programming languages provide ; 10^7 special constructs for specifying it, known as loops. Iteration/Tail Recursion 7 Iteration/Tail Recursion 8

  3. inc_rec in Python inc-iter / inc-tail in Racket (define (inc-iter n) def inc_rec (n): (inc-tail n 1)) if n == 0: return 1 (define (inc-tail num resultSoFar) else: (if (= num 0) return 1 + inc_rec(n - 1) resultSoFar (inc-tail (- num 1) (+ resultSoFar 1)))) In [16]: inc_rec(100) Out[16]: 101 > (inc-iter 10000000) ; 10^7 In [17]: inc_rec(1000) 10000001 … /Users/fturbak/Desktop/lyn/courses/cs251-archive/cs251-s16/slides-lyn-s16/racket-tail/iter.py in inc_rec(n) > (inc-iter 100000000) ; 10^8 9 return 1 10 else: 100000001 ---> 11 return 1 + inc_rec(n - 1) 12 # inc_rec(10) => 11 13 # inc_rec(100) => 101 Will inc-iter ever run out of memory? RuntimeError: maximum recursion depth exceeded Iteration/Tail Recursion 9 Iteration/Tail Recursion 10 inc_iter / int_tail in Python Why the Difference? def inc_iter (n): # Not really iterative! it(0,4) it(0,4): 4 return inc_tail(n, 1) it(1,3) it(1,3) It(1,3) It(1,3): 4 def inc_tail(num, resultSoFar): it(2,2) it(2,2) it(2,2) it(2,2) it(2,2) it(2,2): 4 if num == 0: return resultSoFar it(3,1) it(3,1) it(3,1) it(3,1) it(3,1) it(3,1) it(3,1) it(3,1): 4 else: Python pushes a stack frame for every call to iter_tail. When iter_tail(0,4) returns return inc_tail(num - 1, resultSoFar + 1) the answer 4, the stacked frames must be popped even though no other work remains to be done coming out of the recursion. In [19]: inc_iter(100) Out[19]: 101 it(3,1) It(2,2) It(1,3) It(0,4) It(0,4): 4 In [19]: inc_iter(1000) … Racket’s tail-call op*miza*on replaces the current stack frame with a new stack RuntimeError: maximum recursion depth exceeded frame when a tail call (func*on call not in a subexpression posi*on) is made. When iter-tail(0,4) returns 4, no unnecessarily stacked frames need to be popped! Iteration/Tail Recursion 11 Iteration/Tail Recursion 12

  4. Origins of Tail Recursion What to do in Python (and most other languages)? In Python, must re-express the tail recursion as a loop! def inc_loop (n): resultSoFar = 0 while n > 0: n = n - 1 Guy Lewis Steele resultSoFar = resultSoFar + 1 a.k.a. ``The Great Quux” return resultSoFar In [23]: inc_loop(1000) # 10^3 One of the most important but least appreciated CS papers of all *me Out[23]: 1001 • • Treat a func*on call as a GOTO that passes arguments In [24]: inc_loop(10000000) # 10^8 Out[24]: 10000001 • Func*on calls should not push stack; subexpression evalua*on should! But Racket doesn’t need loop constructs because tail recursion Looping constructs are unnecessary; tail recursive calls are a more general • suffices for expressing itera*on! and elegant way to express itera*on. Iteration/Tail Recursion 13 Iteration/Tail Recursion 14 Itera*ve factorial: Python while loop version while loop factorial: Execu*on Land Itera*on Rules: Execu=on frame for fact_while(4) • next num is previous num minus 1. n num ans • next ans is previous num *mes previous ans. 4 4 1 num = n 4 3 def fact_while(n): ans = 1 2 12 while (num > 0): 24 num = n 1 ans = num * ans Declare/ini=alize local num = num - 1 ans = 1 state variables 0 24 return ans while (num > 0): step num ans ans = num * ans Calculate product and 1 4 1 num = num - 1 decrement num 2 3 4 3 2 12 4 1 24 return ans Don � t forget to return answer! 5 0 24 Iteration/Tail Recursion 15 Iteration/Tail Recursion 16

  5. Rela*ng Tail Recursion and while loops Gotcha! Order of assignments in loop body (define (fact-iter n) What’s wrong with the following loop version of factorial? (fact-tail n 1)) Ini=alize def fact_while(n): variables num = n (define (fact-tail num ans) ans = 1 (if (= num 0) while (num > 0): ans num = num - 1 (fact-tail (- num 1) (* num ans)))) ans = num * ans return ans Moral: must think carefully about order of assignments in loop body! def fact_while(n): While num = n not done, ans = 1 update (define (fact-tail num ans ) while (num > 0): When done, Note: variables (if (= num 0) num = num - 1 return ans tail recursion ans = num * ans doesn’t have ans return ans this gotcha! (fact-tail (- num 1) (* num ans)))) Iteration/Tail Recursion 17 Iteration/Tail Recursion 18 Recursive Fibonacci Itera*on leads to a more efficient Fib The Fibonacci sequence: 0, 1, 1, 2, 3, 5, 8, 13, 21, … (define (fib-rec n) ; returns rabbit pairs at month n Itera*on table for calcula*ng the 8th Fibonacci number: (if (< n 2) ; assume n >= 0 n n i fib_i fib_i_plus_1 (+ (fib-rec (- n 1)) ; pairs alive last month 8 0 0 1 (fib-rec (- n 2)) ; newborn pairs 8 1 1 1 ))) 8 2 1 2 fib(4) : 3 + 8 3 2 3 8 4 3 5 fib(3) : 2 fib(2) : 1 + + 8 5 5 8 8 6 8 13 fib(2) : 1 fib(1) : 1 fib(1) : 1 fib(0) : 0 + 8 7 13 21 fib(1) : 1 fib(0) : 0 8 8 21 34 Iteration/Tail Recursion 19 Iteration/Tail Recursion 20

  6. Itera*ve Fibonacci in Racket Gotcha! Assignment order and temporary variables What’s wrong with the following looping versions of Fibonacci? Flesh out the missing parts def fib_for1(n): fib_i= 0 (define (fib-iter n) fib_i_plus_1 = 1 (fib-tail … )) for i in range(n): fib_i = fib_i_plus_1 (define (fib-tail n i fib_i fib_i_plus_1) fib_i_plus_1 = fib_i + fib_i_plus_1 … return fib_i def fib_for2(n): fib_i= 0 fib_i_plus_1 = 1 for i in range(n): fib_i_plus_1 = fib_i + fib_i_plus_1 ) fib_i = fib_i_plus_1 return fib_i Moral: some*mes no order of assignments to state variables in a loop is correct and it is necessary to introduce one or more temporary variables to save the previous value of a variable for use in the right-hand side of a later assignment. Iteration/Tail Recursion 21 Or can use simultaneous assignment in languages that have it (like Python!) Iteration/Tail Recursion 22 Fixing Gotcha Iterative list summation 1. Use a temporary variable (in general, might need n-1 such vars for n state variables def fib_for_fixed1(n): fib_i= 0 Ø L 6 3 -22 5 fib_i_plus_1 = 1 for i in range(n): fib_i_prev = fib_i Iteration table fib_i = fib_i_plus_1 fib_i_plus = fib_i_prev + fib_i_plus_1 L result return fib_i ‘(6 3 -22 5) 0 ‘(3 -22 5) 6 2. Use simultaneous assignment: ‘(-22 5) 9 def fib_for_fixed2(n): ‘(5) -13 fib_i= 0 ‘() -8 fib_i_plus_1 = 1 for i in range(n): (fib_i, fib_i_plus_1) =\ (fib_i_plus_1, fib_i + fib_i_plus_1) return fib_i Iteration/Tail Recursion 23 Iteration/Tail Recursion 24

Download Presentation
Download Policy: The content available on the website is offered to you 'AS IS' for your personal information and use only. It cannot be commercialized, licensed, or distributed on other websites without prior consent from the author. To download a presentation, simply click this link. If you encounter any difficulties during the download process, it's possible that the publisher has removed the file from their server.

Recommend


More recommend