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 Spring 2019, Lyn Turbak Department of Computer Science Wellesley College Iteration/Tail Recursion 2 An itera*ve approach to factorial Factorial Revisited Invoca'on Tree State Variables: Idea: multiply (fact-rec 4): 24 (define (fact-rec n) on way down • num is the current number being processed. (if (= n 0) pending multiplication is nontrivial glue step -1 4 1 1 * • prod is the product of all numbers already processed. (* n (fact-rec (- n 1))))) glue divide (fact-rec 3): 6 -1 * Itera'on Table: Small-Step Seman'cs -1 * step 3 4 num prod 1 4 1 ({fact-rec} 4) (fact-rec 2): 2 -1 * 2 3 4 � {(λ_fact-rec 4)} divide 3 2 12 � * (* 4 {(λ_fact-rec 3)}) -1 * 2 12 4 1 24 � * (* 4 (* 3 {(λ_fact-rec 2)})) (fact-rec 1): 1 � * (* 4 (* 3 (* 2 {(λ_fact-rec 1)}))) 5 0 24 -1 * � * (* 4 (* 3 (* 2 (* 1 {(λ_fact-rec 0)})))) -1 * � * (* 4 (* 3 (* 2 {(* 1 1)}))) 1 24 Itera'on Rules: � (* 4 (* 3 {(* 2 1)})) (fact-rec 0): 1 • next num is previous num minus 1. � (* 4 {(* 3 2)}) -1 * � {(* 4 6)} • next prod is previous num 'mes previous prod . � 24 24 0 Iteration/Tail Recursion 3 Iteration/Tail Recursion 4

  2. Tail-recursive factorial: Dynamic execu*on Itera*ve factorial: tail recursive version in Racket Invoca'on Tree State Variables: (define (fact-iter n) • num is the current number being processed. (fact-tail n 1)) (fact-iter 4) • prod is the product of all numbers already processed. (define (fact-tail num prod) (fact-tail 4 1) (define (fact-tail num prod ) (if (= num 0) divide (if (= num 0) prod (fact-tail 3 4) (fact-tail (- num 1) (* num prod)))) prod stopping (fact-tail 2 12) (fact-tail (- num 1) (* num prod)))) condition Small-Step Seman'cs (fact-tail 1 24) tail call (no pending operations) expresses iteration rules ({fact-iter} 4) Itera'on Rules: � {(λ_fact-iter 4)} (fact-tail 0 24) Itera'on Table • next num is previous num minus 1. � {(λ_fact-tail 4 1)} no glue! � * {(λ_fact-tail 3 4)} • next prod is previous num 'mes previous prod . step num prod � * {(λ_fact-tail 2 12)} 1 4 1 ;; Here, and in many tail recursions, need a wrapper � * {(λ_fact-tail 1 24)} 2 3 4 ;; function to initialize first row of iteration � * {(λ_fact-tail 0 24)} 3 2 12 ;; table. E.g., invoke (fact-iter 4) to calculate 4! � * 24 4 1 24 (define (fact-iter n) 5 0 24 (fact-tail n 1)) 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 • A process is itera*ve if it can be expressed as a sequence of steps ; function for testing Racket stack memory limits that is repeated un*l some stopping condi*on is reached. (define (inc-rec n) (if (= n 0) • In divide/conquer/glue methodology, an itera*ve 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 > (inc-rec 1000000) ; 10^6 no pending opera*ons aSer the call. When all recursive calls of 1000001 a method are tail calls, it is said to be tail recursive. A tail recursive method is one way to specify an itera*ve process. > (inc-rec 10000000) ; 10^7 Itera*on is so common that most programming languages provide Eventually run out special constructs for specifying it, known as loops. of stack space 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 10000001 In [17]: inc_rec(1000) … in inc_rec(n) > (inc-iter 100000000) ; 10^8 Very small maximum 9 return 1 10 else: recursion depth 100000001 ---> 11 return 1 + inc_rec(n – 1) (implementation dependent) RuntimeError: maximum recursion depth exceeded Will inc-iter ever run out of memory? 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. Although tail recursion In [19]: inc_iter(100) expresses iteration in Racket (and SML), it does *not* Out[19]: 101 express iteration in Python it(3,1) it(2,2) it(1,3) it(0,4) it(0,4): 4 (or JavaScript, C, Java, etc.) 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 prod • next prod is previous num *mes previous prod. 4 4 1 num = n 4 3 def fact_while(n): prod = 1 2 12 while (num > 0): 24 num = n 1 prod = num * prod Declare/ini'alize local num = num - 1 prod = 1 state variables 0 24 return prod while (num > 0): step num prod prod = num * prod Calculate product and 1 4 1 num = num - 1 decrement num 2 3 4 3 2 12 4 1 24 return prod 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): In [23]: fact_while(4) variables num = n Out[23]: 6 (define (fact-tail num prod) prod = 1 (if (= num 0) while (num > 0): prod num = num - 1 (fact-tail (- num 1) (* num prod)))) prod = num * prod return prod Moral: must think carefully about order of assignments in loop body! While def fact_while(n): not done, num = n update prod = 1 (define (fact-tail num prod ) variables while (num > 0): When done, Note: (if (= num 0) prod = num * prod return ans tail recursion num = num – 1 doesn’t have ans return prod this gotcha! (fact-tail (- num 1) (* num prod)))) 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 fibi fibi+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

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