Tail Recursion and Accumulators Recursion Should now be - - PowerPoint PPT Presentation

tail recursion and accumulators recursion
SMART_READER_LITE
LIVE PREVIEW

Tail Recursion and Accumulators Recursion Should now be - - PowerPoint PPT Presentation

Tail Recursion and Accumulators Recursion Should now be comfortable with recursion: No harder than using a loop (Maybe?) OAen much easier than


slide-1
SLIDE 1

Tail ¡Recursion ¡and ¡Accumulators ¡

slide-2
SLIDE 2

Recursion ¡

Should ¡now ¡be ¡comfortable ¡with ¡recursion: ¡

  • No ¡harder ¡than ¡using ¡a ¡loop ¡(Maybe?) ¡

¡

  • OAen ¡much ¡easier ¡than ¡a ¡loop ¡ ¡

– When ¡processing ¡a ¡tree ¡(e.g., ¡evaluate ¡an ¡arithmeFc ¡ expression) ¡ – Avoids ¡mutaFon ¡even ¡for ¡local ¡variables ¡

  • Now: ¡ ¡

– How ¡to ¡reason ¡about ¡efficiency ¡of ¡recursion ¡ – The ¡importance ¡of ¡tail ¡recursion ¡ – Using ¡an ¡accumulator ¡to ¡achieve ¡tail ¡recursion ¡ – [No ¡new ¡language ¡features ¡here] ¡

slide-3
SLIDE 3

Call-­‑stacks ¡

While ¡a ¡program ¡runs, ¡there ¡is ¡a ¡call ¡stack ¡of ¡funcFon ¡ calls ¡that ¡have ¡started ¡but ¡not ¡yet ¡returned ¡

– Calling ¡a ¡funcFon ¡f ¡pushes ¡an ¡instance ¡of ¡f on ¡the ¡stack ¡ – When ¡a ¡call ¡to ¡f to ¡finishes, ¡it ¡is ¡popped ¡from ¡the ¡stack ¡

These ¡stack ¡frames ¡store ¡informaFon ¡such ¡as ¡

  • the ¡values ¡of ¡arguments ¡and ¡local ¡variables ¡
  • informaFon ¡about ¡“what ¡is ¡leA ¡to ¡do” ¡in ¡the ¡funcFon ¡

(further ¡computaFons ¡to ¡do ¡with ¡results ¡from ¡other ¡ funcFon ¡calls) ¡ Due ¡to ¡recursion, ¡mulFple ¡stack-­‑frames ¡may ¡be ¡calls ¡to ¡ the ¡same ¡funcFon ¡

slide-4
SLIDE 4

Example ¡

(define (fact n) (if (= n 0) 1 (* n (fact (- n 1)))))

(fact ct 3 3) = => ( (* 3 3 _) _) (fact ct 3 3) (fact ct 2 2) (fact 3) => (* 3 _) (fact ct 0 0) (fact ct 1 1) (fact 2) => (* 2 _) (fact 1) => (* 1 _) (fact 2) => (* 2 _) (fact 3) => (* 3 _) (fact ct 3 3) = => ( (* 3 3 _) _) (fact ct 3 3) = => ( (* 3 3 2 2) (fact ct 2 2) = => ( (* 2 2 1 1) (fact 3) => (* 3 _) (fact ct 0 0) = => 1 1 (fact ct 1 1) = => ( (* 1 1 1 1) (fact 2) => (* 2 _) (fact 1) => (* 1 _) (fact 2) => (* 2 _) (fact 3) => (* 3 _)

slide-5
SLIDE 5

What's ¡being ¡computed ¡

(fact 3) => (* 3 (fact 2)) => (* 3 (* 2 (fact 1))) => (* 3 (* 2 (* 1 (fact 0)))) => (* 3 (* 2 (* 1 1))) => (* 3 (* 2 1)) => (* 3 2) => 6

slide-6
SLIDE 6

Example ¡Revised ¡

(define (fact2 n) (define (fact2-helper n acc) (if (= n 0) acc (fact2-helper (- n 1) (* acc n)))) (fact2-helper n 1))

SFll ¡recursive, ¡more ¡complicated, ¡but ¡the ¡result ¡of ¡ recursive ¡calls ¡is ¡the ¡result ¡for ¡the ¡caller ¡(no ¡ remaining ¡mulFplicaFon) ¡

slide-7
SLIDE 7

Example ¡Revised ¡

(fact ct2 3 3) = => _ _ (fact ct2 3 3) (f2-h 3 3 1 1) (fact2 3) => _ (f2-h 1 1 6 6) (f2-h 2 2 3 3) (f2-h 3 1) => _ (f2-h 2 3) => _ (f2-h 3 1) => _ (fact2 3) => _ (fact2 3) => _ (fact2 3) => _ (f2-h 3 1) => _ (fact2 3) => _ (f2-h 1 1 6 6) = => _ _ (f2-h 2 3) => _ (f2-h 3 1) => _ (f2-h 2 3) => _ (f2-h 3 1) => _ (fact2 3) => _

(define (fact2 n) (define (fact2-helper n acc) (if (= n 0) acc (fact2-helper (- n 1) (* acc n)))) (fact2-helper n 1))

(f2-h 0 0 6 6) (f2-h 3 1) => _ (f2-h 2 3) => _ (f2-h 1 1 6 6) = => _ _ (f2-h 0 0 6 6) = => 6 6 (f2-h 1 1 6 6) = => 6 6 (f2-h 2 3) => 6

slide-8
SLIDE 8

What's ¡being ¡computed ¡

(fact2 3) => (fact2-helper 3 1) => (fact2-helper 2 3) => (fact2-helper 1 6) => (fact2-helper 0 6) => 6

slide-9
SLIDE 9

An ¡opFmizaFon ¡

It ¡is ¡unnecessary ¡to ¡keep ¡around ¡a ¡stack-­‑frame ¡just ¡so ¡it ¡can ¡ get ¡a ¡callee’s ¡result ¡and ¡return ¡it ¡without ¡any ¡further ¡ evaluaFon ¡ ¡ Racket ¡recognizes ¡these ¡tail ¡calls ¡in ¡the ¡compiler ¡and ¡treats ¡ them ¡differently: ¡

– Pop ¡the ¡caller ¡before ¡the ¡call, ¡allowing ¡callee ¡to ¡reuse ¡the ¡same ¡ stack ¡space ¡ – (Along ¡with ¡other ¡opFmizaFons,) ¡as ¡efficient ¡as ¡a ¡loop ¡

(Reasonable ¡to ¡assume ¡all ¡funcFonal-­‑language ¡ implementaFons ¡do ¡tail-­‑call ¡opFmizaFon) ¡

¡includes ¡Racket, ¡Scheme, ¡LISP, ¡ML, ¡Haskell, ¡OCaml… ¡

slide-10
SLIDE 10

What ¡really ¡happens ¡

(fact 3) (f2-h 3 1) (f2-h 2 3) (f2-h 1 6) (f2-h 0 6)

(define (fact2 n) (define (fact2-helper n acc) (if (= n 0) acc (fact2-helper (- n 1) (* acc n)))) (fact2-helper n 1))

slide-11
SLIDE 11

Moral ¡

  • Where ¡reasonably ¡elegant, ¡feasible, ¡and ¡important, ¡

rewriFng ¡funcFons ¡to ¡be ¡tail-­‑recursive ¡can ¡be ¡much ¡more ¡ efficient ¡

– Tail-­‑recursive: ¡recursive ¡calls ¡are ¡tail-­‑calls ¡ ¡

  • meaning ¡all ¡recursive ¡calls ¡must ¡be ¡the ¡last ¡thing ¡the ¡calling ¡funcFon ¡

does ¡

  • no ¡addiFonal ¡computaFon ¡with ¡the ¡result ¡of ¡the ¡callee ¡
  • There ¡is ¡also ¡a ¡methodology ¡to ¡guide ¡this ¡transformaFon: ¡

– Create ¡a ¡helper ¡funcFon ¡that ¡takes ¡an ¡accumulator ¡ – Old ¡base ¡case's ¡return ¡value ¡becomes ¡iniFal ¡accumulator ¡value ¡ – Final ¡accumulator ¡value ¡becomes ¡new ¡base ¡case ¡return ¡value ¡

slide-12
SLIDE 12

(define (fact2 n) (define (fact2-helper n acc) (if (= n 0) acc (fact2-helper (- n 1) (* acc n)))) (fact2-helper n 1)) (define (fact n) (if (= n 0) 1 (* n (fact (- n 1)))))

Final ¡accumulator ¡value ¡ becomes ¡new ¡base ¡case ¡ return ¡value. ¡ Old ¡base ¡case's ¡return ¡ value ¡becomes ¡iniFal ¡ accumulator ¡value. ¡

slide-13
SLIDE 13

Another ¡example ¡

(define (sum1 lst) (if (null? lst) 0 (+ (car lst) (sum1 (cdr lst))))) (define (sum2 lst) (define (sum2-helper lst acc) (if (null? lst) acc (sum2-helper (cdr lst) (+ (car lst) acc)))) (sum2-helper lst 0))

slide-14
SLIDE 14

And ¡another ¡

(define (rev1 lst) (if (null? lst) '() (append (rev1 (cdr lst)) (list (car lst))))) (define (rev2 lst) (define (rev2-helper lst acc) (if (null? lst) acc (rev2-helper (cdr lst) (cons (car lst) acc)))) (rev2-helper lst '()))

slide-15
SLIDE 15

Actually ¡much ¡be_er ¡

  • For ¡fact ¡and ¡sum, ¡tail-­‑recursion ¡is ¡faster ¡but ¡both ¡ways ¡

linear ¡Fme ¡

  • The ¡non-­‑tail ¡recursive ¡rev ¡is ¡quadraFc ¡because ¡each ¡

recursive ¡call ¡uses ¡append, ¡which ¡must ¡traverse ¡the ¡first ¡list ¡

– And ¡1 ¡+ ¡2 ¡+ ¡… ¡+ ¡(length-­‑1) ¡is ¡almost ¡length ¡* ¡length ¡/ ¡2 ¡ ¡ – Moral: ¡beware ¡append, ¡especially ¡if ¡1st ¡argument ¡is ¡result ¡of ¡a ¡ recursive ¡call ¡

  • cons ¡is ¡constant-­‑Fme ¡(and ¡fast), ¡so ¡the ¡accumulator ¡version ¡

rocks ¡

(define (rev1 lst) ; Bad version (non T-R) (if (null? lst) '() (append (rev1 (cdr lst)) (list (car lst)))))

slide-16
SLIDE 16

Tail-­‑recursion ¡== ¡while ¡loop ¡with ¡local ¡ variable ¡

(define (fact2 n) (define (fact2-helper n acc) (if (= n 0) acc (fact2-helper (- n 1) (* acc n)))) (fact2-helper n 1)) def fact2(n): acc = 1 while n != 0: acc = acc * n n = n – 1 return acc

slide-17
SLIDE 17

Tail-­‑recursion ¡== ¡while ¡loop ¡with ¡local ¡ variable ¡

(define (sum2 lst) (define (sum2-helper lst acc) (if (null? lst) acc (sum2-helper (cdr lst) (+ (car lst) acc)))) (sum2-helper lst 0)) def sum2(lst): acc = 0 while lst != []: acc = lst[0] + acc lst = lst[1:] return acc

slide-18
SLIDE 18

Tail-­‑recursion ¡== ¡while ¡loop ¡with ¡local ¡ variable ¡

(define (rev2 lst) (define (rev2-helper lst acc) (if (null? lst) acc (rev2-helper (cdr lst) (cons (car lst) acc)))) (rev2-helper lst '())) def rev2(lst): acc = [] while lst != []: acc = [lst[0]] + acc lst = lst[1:] return acc

slide-19
SLIDE 19

Always ¡tail-­‑recursive? ¡

There ¡are ¡certainly ¡cases ¡where ¡recursive ¡funcFons ¡ cannot ¡be ¡evaluated ¡in ¡a ¡constant ¡amount ¡of ¡space ¡ ¡ Example: ¡funcFons ¡that ¡process ¡trees ¡

– Lists ¡can ¡be ¡used ¡to ¡ ¡ represent ¡trees: ¡'((1 2) ((3 4) 5))

¡ In ¡these ¡cases, ¡the ¡natural ¡recursive ¡approach ¡is ¡the ¡ way ¡to ¡go ¡

– You ¡could ¡get ¡one ¡recursive ¡call ¡to ¡be ¡a ¡tail ¡call, ¡but ¡ rarely ¡worth ¡the ¡complicaFon ¡ ¡

¡ 1 ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡2 ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡5 ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡3 ¡ ¡ ¡ ¡ ¡4 ¡

slide-20
SLIDE 20

Precise ¡definiFon ¡

If ¡the ¡result ¡of ¡(f x) is ¡the ¡“return ¡value” ¡for ¡the ¡enclosing ¡funcFon ¡body, ¡ then ¡(f x) ¡is ¡a ¡tail ¡call ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡i.e., ¡don't ¡have ¡to ¡do ¡any ¡more ¡processing ¡of ¡(f ¡x) ¡to ¡end ¡funcFon ¡ ¡ Can ¡define ¡this ¡noFon ¡more ¡precisely… ¡

  • A ¡tail ¡call ¡is ¡a ¡funcFon ¡call ¡in ¡tail ¡posi6on ¡
  • The ¡single ¡expression ¡(ignoring ¡nested ¡defines) ¡of ¡the ¡body ¡of ¡a ¡funcFon ¡

is ¡in ¡tail ¡posiFon. ¡

  • If ¡(if test e1 e2) ¡is ¡in ¡tail ¡posiFon, ¡then ¡e1 ¡and ¡e2 ¡are ¡in ¡tail ¡

posiFon ¡(but ¡test ¡is ¡not). ¡ ¡(Similar ¡for ¡cond-­‑expressions) ¡

  • If ¡a ¡let-­‑expression ¡is ¡in ¡tail ¡posiFon, ¡then ¡the ¡single ¡expression ¡of ¡the ¡

body ¡of ¡the ¡let ¡is ¡in ¡tail ¡posiFon ¡(but ¡no ¡variable ¡bindings ¡are) ¡

  • Arguments ¡to ¡a ¡funcFon ¡call ¡are ¡not ¡in ¡tail ¡posiFon ¡
  • … ¡

¡

slide-21
SLIDE 21

Are ¡these ¡funcFons ¡tail-­‑recursive? ¡

(define (get-nth lst n) (if (= n 0) (car lst) (get-nth (cdr lst) (- n 1)))) (define (good-max lst) (cond ((null? (cdr lst)) (car lst)) (#t (let ((max-of-cdr (good-max (cdr lst)))) (if (> (car lst) max-of-cdr) (car lst) max-of-cdr)))))

slide-22
SLIDE 22

Try ¡these… ¡

Write ¡a ¡tail-­‑recursive ¡max ¡funcFon ¡(i.e., ¡a ¡funcFon ¡that ¡returns ¡the ¡ largest ¡element ¡in ¡a ¡list). ¡ ¡ Write ¡a ¡tail-­‑recursive ¡Fibonacci ¡sequence ¡funcFon ¡(i.e., ¡a ¡funcFon ¡ that ¡returns ¡the ¡n'th ¡number ¡of ¡the ¡Fibonacci ¡sequence). ¡ ¡(fib 1) => 1 (fib 2) => 1 (fib 3) => 2 (fib 4) => 3 (fib 5) => 5 ¡In ¡general, ¡(fib n) = (+ (fib (- n 1)) (fib (- n 2)))

slide-23
SLIDE 23

(define (maxtr lst) (define (maxtr-helper lst max-so-far) (cond ((null? lst) max-so-far) ((> max-so-far (car lst)) (maxtr-helper (cdr lst) max-so-far)) (#t (maxtr-helper (cdr lst) (car lst))))) (maxtr-helper (cdr lst) (car lst)))

slide-24
SLIDE 24

(define (fib-tr n) (define (fib-helper a b ctr) (if (= ctr n) a (fib-helper b (+ a b) (+ ctr 1)))) (fib-helper 1 1 1))