Overview. Iteration via Tail Recursion in Racket SOLUTIONS. Factorial Revisited. An itera*ve approach to factorial

Similar documents
Overview. Iteration via Tail Recursion in Racket. Factorial Revisited. An itera*ve approach to factorial. What is itera*on?

Overview. Factorial Revisited. Iteration via Tail Recursion in Racket. An itera*ve approach to factorial. What is itera*on?

Func+on applica+ons (calls, invoca+ons)

CS251 Programming Languages Spring 2016, Lyn Turbak Department of Computer Science Wellesley College

Recursive List Func.ons in Racket

61A LECTURE 22 TAIL CALLS, ITERATORS. Steven Tang and Eric Tzeng July 30, 2013

Racket Values. cons Glues Two Values into a Pair. The Pros of cons: Programming with Pairs and Lists. Box-and-pointer diagrams for cons trees

Algorithm Design and Recursion. Search and Sort Algorithms

Fruitful Recursion (recursion that returns a value)

CS116 - Module 5 - Accumulative Recursion

Functional Programming - 2. Higher Order Functions

RACKET BASICS, ORDER OF EVALUATION, RECURSION 1

Admin. How's the project coming? After these slides, read chapter 13 in your book. Quizzes will return

CS 340 Spring 2019 Midterm Exam

ITERATORS AND STREAMS 9

PostFix. PostFix command semanccs (except exec) PostFix Syntax. A PostFix Interpreter in Racket. CS251 Programming Languages Spring 2018, Lyn Turbak

CS251 Programming Languages Spring 2016, Lyn Turbak Department of Computer Science Wellesley College

Interpreters and Tail Calls Fall 2017 Discussion 8: November 1, 2017 Solutions. 1 Calculator. calc> (+ 2 2) 4

Summer 2017 Discussion 10: July 25, Introduction. 2 Primitives and Define

CS1 Recitation. Week 2

Module 10: Imperative Programming, Modularization, and The Future

Recursion(int day){return Recursion(day += 1);} Comp Sci 1575 Data Structures. Recursive design. Convert loops to recursion

Macros & Streams Spring 2018 Discussion 9: April 11, Macros

PostFix. PostFix command semanccs (except exec) PostFix Syntax. A PostFix Interpreter in Racket. CS251 Programming Languages Fall 2017, Lyn Turbak

Delayed Expressions Fall 2017 Discussion 9: November 8, Iterables and Iterators. For Loops. Other Iterable Uses

RECURSION, RECURSION, (TREE) RECURSION! 3

SCHEME 10 COMPUTER SCIENCE 61A. July 26, Warm Up: Conditional Expressions. 1. What does Scheme print? scm> (if (or #t (/ 1 0)) 1 (/ 1 0))

TAIL RECURSION, SCOPE, AND PROJECT 4 11

Tail Recursion. ;; a recursive program for factorial (define fact (lambda (m) ;; m is non-negative (if (= m 0) 1 (* m (fact (- m 1))))))

301AA - Advanced Programming

Scheme as implemented by Racket

Introduc)on To Standard ML

Functions. CS10001: Programming & Data Structures. Sudeshna Sarkar Professor, Dept. of Computer Sc. & Engg., Indian Institute of Technology Kharagpur

INTERPRETERS 8. 1 Calculator COMPUTER SCIENCE 61A. November 3, 2016

Tail Recursion and Accumulators

TAIL CALLS, ITERATORS, AND GENERATORS 11

6.001 Notes: Section 4.1

A brief tour of history

Principles of Programming Languages

Generative and accumulative recursion. What is generative recursion? Example revisited: GCD. Readings: Sections 25, 26, 27, 30, 31

SCHEME 8. 1 Introduction. 2 Primitives COMPUTER SCIENCE 61A. March 23, 2017

RECURSION, RECURSION, (TREE) RECURSION! 2

Recursion CS GMU

RECURSION 7. 1 Recursion COMPUTER SCIENCE 61A. October 15, 2012

CSCE 314 Programming Languages

Shell CSCE 314 TAMU. Higher Order Functions

Introduction to Functional Programming

INTRODUCTION TO HASKELL

What is recursion? Recursion. How can a function call itself? Recursive message() modified. Week 10. contains a reference to itself.

CS 209 Functional Programming

Resources matter. Orders of Growth of Processes. R(n)= (n 2 ) Orders of growth of processes. Partial trace for (ifact 4) Partial trace for (fact 4)

Computer Science 210 Data Structures Siena College Fall Topic Notes: Recursive Methods

First-Class Synchronization Barriers. Franklyn Turbak Wellesley College

Iteration Part 1. Motivation for iteration. How does a for loop work? Execution model of a for loop. What is Iteration?

Chapter 1. Fundamentals of Higher Order Programming

ITERATION AND RECURSION 3

INTERPRETERS AND TAIL CALLS 9

FOFL and FOBS: First-Order Functions

COMP 202 Recursion. CONTENTS: Recursion. COMP Recursion 1

Fall 2017 Discussion 7: October 25, 2017 Solutions. 1 Introduction. 2 Primitives

RECURSION: n. SEE RECURSION 3

Functions and Recursion. Dr. Philip Cannata 1

RECURSION, RECURSION, (TREE) RECURSION! 3

Lecture #24: Programming Languages and Programs

CS450 - Structure of Higher Level Languages

What is an algorithm?

Use recursion to write a function that duplicates the following function: (def (f L) (map (lambda (x) (+ (sqr x) x)) L))

Encodings. Using the minimal λ-calculus language we get. functions. local binding. booleans. numbers... and recursive functions?

Booleans (aka Truth Values) Programming Languages and Compilers (CS 421) Booleans and Short-Circuit Evaluation. Tuples as Values.

Spring 2018 Discussion 7: March 21, Introduction. 2 Primitives

Week - 03 Lecture - 18 Recursion. For the last lecture of this week, we will look at recursive functions. (Refer Slide Time: 00:05)

List Processing in SML

Module 05: Types of recursion

Identify recursive algorithms Write simple recursive algorithms Understand recursive function calling

CSC 533: Programming Languages. Spring 2015

CS 206 Introduction to Computer Science II

Comp215: More Recursion

Homework 5. Notes. Turn-In Instructions. Reading. Problems. Handout 19 CSCI 334: Spring (Required) Read Mitchell, Chapters 6 and 7.

Introduction to Functional Programming in Racket. CS 550 Programming Languages Jeremy Johnson

CSI33 Data Structures

PROGRAMMING IN HASKELL. CS Chapter 6 - Recursive Functions

CSE 341 : Programming Languages Midterm, Spring 2015

Recursion. Genome 559: Introduction to Statistical and Computational Genomics Elhanan Borenstein

Module 8: Local and functional abstraction

# true;; - : bool = true. # false;; - : bool = false 9/10/ // = {s (5, "hi", 3.2), c 4, a 1, b 5} 9/10/2017 4

Recursive Definitions

List Processing in SML

CSci 4223 Principles of Programming Languages

RECURSION 3. 1 Recursion COMPUTER SCIENCE 61A. June 30, 2016

Types of recursion. Structural vs. general recursion. Pure structural recursion. Readings: none. In this module: learn to use accumulative recursion

Streams and Evalutation Strategies

Structure and Interpretation of Computer Programs

Recursion versus Iteration


CS 61A Interpreters, Tail Calls, Macros, Streams, Iterators. Spring 2019 Guerrilla Section 5: April 20, Interpreters.

Programming Languages. Streams Wrapup, Memoization, Type Systems, and Some Monty Python

A function that invokes itself is said to

Structure and Interpretation of Computer Programs

CSCC24 Functional Programming Scheme Part 2

SCHEME 7. 1 Introduction. 2 Primitives COMPUTER SCIENCE 61A. October 29, 2015

Transcription:

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

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

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

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

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

Itera*ve Fibonacci in Racket Flesh out the missing parts (define (fib-iter n) (fib-tail n 0 0 1) ) (define (fib-tail n i fibi fibi+1) (if (= i n) fibi (fib-tail n (+ i 1) fibi+1 (+ fibi fibi+1))) ) Iteration/Tail Recursion 21 Gotcha! Assignment order and temporary variables What s wrong with the following looping versions of Fibonacci? def fib_for1(n): fib_i= 0 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 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. Or can use simultaneous assignment in languages that have it (like Python!) Iteration/Tail Recursion 22 Fixing Gotcha 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 fib_i_plus_1 = 1 for i in range(n): fib_i_prev = fib_i fib_i = fib_i_plus_1 fib_i_plus = fib_i_prev + fib_i_plus_1 return fib_i 2. Use simultaneous assignment: def fib_for_fixed2(n): fib_i= 0 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 Local fib-tail func*on in fib-iter Can define fib-tail locally within fib-iter. Since n remains constant, don t need it as an argument to local fib-tail. (define (fib-iter n) (define (fib-tail i fibi fibi+1) (if (= i n) fibi (fib-tail (+ i 1) fibi+1 (+ fibi fibi+1)))) (fib-tail 0 0 1) ) Iteration/Tail Recursion 24

Itera*ve List Summa*on L 6 3-22 5 Ø (define (sumlist-iter L) (sumlist-tail L 0 )) Iteration table nums sumsofar (6 3-22 5) 0 (3-22 5) 6 (-22 5) 9 (5) -13 () -8 (define (sumlist-tail nums sumsofar) (if (null? nums) sumsofar (sumlist-tail (rest nums) (+ (first nums) sumsofar)))) Iteration/Tail Recursion 25 Capturing list itera*on via my-foldl v 1 v 2 v n-1 v n initval combine combine combine combine (initval is the ini*al resultsofar) (define (my-foldl combine resultsofar xs) (if (null? xs) resultsofar (my-foldl combiner (combine (first xs) resultsofar) (rest xs)))) Iteration/Tail Recursion 26 combine foldr vs foldl combine combine combine v 1 v 2 v n-1 v n initval combine combine combine combine nullval Iteration/Tail Recursion 27 my-foldl Examples > (my-foldl + 0 (list 7 2 4)) 13 > (my-foldl * 1 (list 7 2 4)) 56 > (my-foldl cons null (list 7 2 4)) '(4 2 7) > (my-foldl (λ (n res) (+ (* 3 res) n)) 0 (list 10-4 5 2)) 251 ; = 10*3^3 + -4*3^2 + 5*3^1 + 2*3&0 ; An example of Horner s method ; for polynomial evaluation Iteration/Tail Recursion 28

Built-in Racket foldl Func*on Folds over Any Number of Lists > (foldl cons null (list 7 2 4)) '(4 2 7) > (foldl (λ (a b res) (+ (* a b) res)) 56 0 (list 2 3 4) (list 5 6 7)) Itera*ve vs Recursive List Reversal (define (reverse-iter xs) (foldl cons null xs)) (define (snoc x ys) (foldr cons (list x) ys)) (define (reverse-rec xs) (foldr snoc null xs)) > (foldl (λ (a b res) (+ (* a b) res)) 0 (list 1 2 3 4) (list 5 6 7)) Same design decision as in map and foldr > ERROR: foldl: given list does not have the same size as the first list: '(5 6 7) Iteration/Tail Recursion 29 How do these compare in terms of the number of conses performed for a list of length 100? 1000? n? Ans: reverse-iter: exactly n conses snoc: exactly n+1 conses reverse-rec: quadra=c (O(n^2)) conses Iteration/Tail Recursion 30 What does this do? Tail Recursion Review 1 (define (whatisit f xs) (foldl (λ (x listsofar) (cons (f x) listsofar)) null xs))) # Euclid s algorithm def gcd(a,b): while b!= 0: temp = b b = a % b a = temp return a 1. Create an iteration table for gcd(42,72) 2. Translate Python gcd into Racket tail recursion. Ans: It performs the ``reverse map of func=on f on list xs. E.g., (whatisit (λ (n) (* n 3)) '(7 2 4)) => '(12 6 21) To perform a regular map, change foldl to foldr! Iteration/Tail Recursion 31 a b 42 72 72 42 42 30 30 12 12 6 6 0 (define (gcd a b) (if (= b 0) a (gcd b (remainder a b)))) Iteration/Tail Recursion 32

Tail Recursion Review 2 def toint(digits): i = 0 for d in digits: num = 10*i + d return i digits [1,7,2,9] 0 [7,2,9] 1 [2,9] 17 [9] 172 [] 1729 i 1. Create an iteration table for toint([1,7,2,9]) 2. Translate Python toint into Racket tail recursion. 3. Translate Python toint into Racket foldl. (define (toint digits) (define (tointtail ds i) (if (null? ds) i (tointtail (rest ds) (+ (* 10 i) d)))) (tointtail digits 0)) (define (toint digits) (foldl (λ (d i) (+ (* 10 i) d)) 0 digits)) Iteration/Tail Recursion 33 iterate (define (iterate next done? finalize state) (if (done? state) (finalize state) (iterate next done? finalize (next state)))) For example: (define (fact-iterate n) (iterate (λ (num&prod) (list (- (first num&prod) 1) (* (first num&prod) (second num&prod)))) (λ (num&prod) (<= (first num&prod) 0)) (λ (num&prod) (second num&prod)) (list n 1))) step num prod 1 4 1 2 3 4 3 2 12 4 1 24 5 0 24 step num&prod 1 '(4 1) 2 '(3 4) 3 '(2 12) 4 '(1 24) 5 '(0 24) Iteration/Tail Recursion 34 ; Soln 1 (define (least-power-geq base threshold) (iterate (λ (pow) (* base pow)) (λ (pow) (>= pw threshold)) (λ (pow) pow) 1)) ; Initial power ; Soln 2 (define (least-power-geq base threshold) (iterate (λ (exp) (+ exp 1)) (λ (exp) (>= (expt base exp) threshold)) (λ (exp) (expt base exp)) ; To return just exponent, ; use exp here. 0)) ; Initial exponent > (least-power-geq 2 10) 16 > (least-power-geq 5 100) 125 > (least-power-geq 3 100) 243 least-power-geq In Soln 2, just return exp rather than (expt base exp) In Soln 1, change state to list of (1) power and (2) exponent. exponent is initialized to 0 and is incremented at each step. In finalization step, return exponent. How could we return just the exponent rather than the base raised to the exponent? Iteration/Tail Recursion 35 What do These Do? (define (mystery1 n) ; Assume n >= 0 (iterate (λ (ns) (cons (- (first ns) 1) ns)) (λ (ns) (<= (first ns) 0)) (λ (ns) ns) (list n))) (define (mystery2 n) (iterate (λ (ns) (cons (quotient (first ns) 2) ns)) (λ (ns) (<= (first ns) 1)) (λ (ns) (- (length ns) 1)) (list n))) mystery1 returns the list of ints from 0 up to and including n. E.g., (mystery1 5) => '(0 1 2 3 4 5) mystery2 calculates the log-base-2 of n by determining how many =mes n can be divided by 2 before reaching 1. E.g. (mystery2 32) => 5 Iteration/Tail Recursion 36

Using let to introduce local names (define (fact-let n) (iterate (λ (num&prod) (let ([num (first num&prod)] [prod (second num&prod)]) (list (- num 1) (* num prod)))) (λ (num&prod) (<= (first num&prod) 0)) (λ (num&prod) (second num&prod)) (list n 1))) Using match to introduce local names (define (fact-match n) (iterate (λ (num&prod) (match num&prod [(list num prod) (list (- num 1) (* num prod))])) (λ (num&prod) (match num&prod [(list num prod) (<= num 0)])) (λ (num&prod) (match num&prod [(list num prod) prod])) (list n 1))) Iteration/Tail Recursion 37 Iteration/Tail Recursion 38 (define (avg a b) (/ (+ a b) 2)) > (avg 6 10) 8 > (apply avg '(6 10)) 8 Racket s apply > ((λ (a b c) (+ (* a b) c)) 2 3 4) 10 > (apply (λ (a b c) (+ (* a b) c)) (list 2 3 4)) 10 apply takes (1) a func*on and (2) a single argument that is a list of values and returns the result of applying the func*on to the values. Iteration/Tail Recursion 39 iterate-apply: a kinder, gentler iterate (define (iterate-apply next done? finalize state) (if (apply done? state) (apply finalize state) (iterate-apply next done? finalize (apply next state)))) (define (fact-iterate-apply n) (iterate-apply (λ (num prod) (list (- num 1) (* num prod))) (λ (num prod) (<= num 0)) (λ (num prod) prod) (list n 1))) step 1 num 4 prod 1 2 3 4 3 2 12 4 1 24 5 0 24 Iteration/Tail Recursion 40

iterate-apply: fib and gcd (define (fib-iterate-apply n) (iterate-apply (λ (i fibi fibi+1) ; next (list (+ i 1) fibi+1 (+ fibi fibi+1)) (λ (i fibi fibi+1) (= i n)) ; done? (λ (i fibi fibi+1) fib_i) ; finalize (list 0 0 1) )) (define (gcd-iterate-apply a b) (iterate-apply (lambda (a b) (list b (remainder a b)) (lambda (a b) (= b 0) (lambda (a b) a) (list a b))) )) ; init state ; next ; done? ; finalize ; init state n i fibi fibi+1 8 0 0 1 8 1 1 1 8 2 1 2 8 3 2 3 8 4 3 5 8 5 5 8 8 6 8 13 8 7 13 21 8 8 21 34 a Iteration/Tail Recursion 41 b 42 72 72 42 42 30 30 12 12 6 6 0 seed V1 next done? #f Crea*ng lists with genlist V2 next done? #f Vpenult next Vdone? done? (define (genlist next done? keepdonevalue? seed) (if (done? seed) (if keepdonevalue? (list seed) null) (cons seed (genlist next done? keepdonevalue? (next seed))))) not itera*ve as wrioen, but next func*on gives itera*ve ``flavor #f done? #t Keep Vdone only if keepdonevalue? Is true Iteration/Tail Recursion 42 Simple genlist examples What are the values of the following calls to genlist? (genlist (λ (n) (- n 1)) (λ (n) (= n 0)) #t 5) (genlist (λ (n) (* n 2)) (λ (n) (> n 100)) #t 1) (genlist (λ (n) (- n 1)) (λ (n) (= n 0)) #f 5) '(5 4 3 2 1 0) '(5 4 3 2 1) (genlist (λ (n) (* n 2)) (λ (n) (> n 100)) #f 1) '(1 2 4 8 16 32 64 128) '(1 2 4 8 16 32 64) Iteration/Tail Recursion 43 genlist: my-range and halves (my-range lo hi) > (my-range 10 15) '(10 11 12 13 14) > (my-range 20 10) '() (halves num) > (halves 64) '(64 32 16 8 4 2 1) > (halves 42) '(42 21 10 5 2 1) > (halves -63) '(-63-31 -15-7 -3-1) (define (my-range-genlist lo hi) (genlist (λ (n) (+ n 1)) ; next (λ (n) (>= n hi)) ; done? #f ; keepdonevalue? lo )) (define (halves num) (genlist ; seed (λ (n) (quotient n 2)) ; next (λ (n) (= n 0)) ; done? #f ; keepdonevalue? num )) ; seed Iteration/Tail Recursion 44

Using genlist to generate itera*on tables (define (fact-table n) (genlist (λ (num&prod) (let ((num (first num&ans)) (prod (second num&ans))) (list (- num 1) (* num prod)))) (λ (num&prod) (<= (first num&prod) 0)) #t (list n 1))) step num prod 1 4 1 2 3 4 3 2 12 4 1 24 5 0 24 > (fact-table 4) '((4 1) (3 4) (2 12) (1 24) (0 24)) > (fact-table 5) '((5 1) (4 5) (3 20) (2 60) (1 120) (0 120)) > (fact-table 10) '((10 1) (9 10) (8 90) (7 720) (6 5040) (5 30240) (4 151200) (3 604800) (2 1814400) (1 3628800) (0 3628800)) 45 Your turn: sum-list itera*on table (define (sum-list-table ns) (genlist (λ (nums&sum) ; next (let {[nums (first nums&ans)] [sum (second nums&ans)]} (list (rest nums) (+ sum (first nums))))) (λ (nums&sum) ; done? (null? (first nums&sum))) #t ; keepdonevalue? (list ns 0)) ; seed ) > (sum-list-table '(7 2 5 8 4)) '(((7 2 5 8 4) 0) ((2 5 8 4) 7) ((5 8 4) 9) ((8 4) 14) ((4) 22) (() 26)) Iteration/Tail Recursion 46 genlist can collect itera*on table column! ; With table abstraction (define (partial-sums ns) (map second (sum-list-table ns))) ; Without table abstraction (define (partial-sums ns) (map second (genlist (λ (nums&sum) (let ((nums (first nums&ans)) (sum (second nums&ans))) (list (rest nums) (+ (first nums) sum)))) (λ (nums&sum) (null? (first nums&sum))) #t (list ns 0)))) > (partial-sums '(7 2 5 8 4)) '(0 7 9 14 22 26) Moral: ask yourself the ques*on Can I generate this list as the column of an itera*on table? Iteration/Tail Recursion 47 genlist-apply: a kinder, gentler genlist (define (genlist-apply next done? keepdonevalue? seed) (if (apply done? seed) (if keepdonevalue? (list seed) null) (cons seed (genlist-apply next done? keepdonevalue? (apply next seed))))) Example: (define (partial-sums ns) (map second (genlist-apply (λ (nums ans) (list (rest nums) (+ (first nums) ans))) (λ (nums ans) (null? nums)) #t (list ns 0)))) Iteration/Tail Recursion 48

partial-sums-between (define (partial-sums-between lo hi) (map second (genlist-apply (λ (num sum) ; next (list (+ num 1) (+ num sum))) (λ (num sum) ; done? (> num hi)) #t ; keepdonevalue? (list lo 0) ; seed ))) Itera*ve Version of genlist ;; Returns the same list as genlist, but requires only ;; constant stack depth (*not* proportional to list length) (define (genlist-iter next done? keepdonevalue? seed) (iterate-apply (λ (state reversedstatessofar) (list (next state) (cons state reversedstatessofar))) (λ (state reversedstatessofar) (done? state)) (λ (state reversedstatessofar) (if keepdonevalue? (reverse (cons state reversedstatessofar)) (reverse reversedstatessofar))) (list seed '()))) > (partial-sums-between 3 7) '(0 3 7 12 18 25) > (partial-sums-between 1 10) '(0 1 3 6 10 15 21 28 36 45 55) Iteration/Tail Recursion 49 Example: How does this work? (genlist-iter (λ (n) (quotient n 2)) (λ (n) (<= n 0)) 5) Iteration/Tail Recursion 50 Itera*ve Version of genlist-apply (define (genlist-apply-iter next done? keepdonevalue? seed) (iterate-apply (λ (state reversedstatessofar) (list (apply next state) (cons state reversedstatessofar))) (λ (state reversedstatessofar) (apply done? state)) (λ (state reversedstatessofar) (if keepdonevalue? (reverse (cons state reversedstatessofar)) (reverse reversedstatessofar))) (list seed '()))) Iteration/Tail Recursion 51