Lecture 13, July 17
The lecture notes I post serve two purposes: to remind you of the topics we discussed and to provide any interesting code examples I did during lecture. I would describe these notes as an outline, not a summary, of what I talked about during lecture. You are not expected to be able to learn the material simply from examining these notes, nor is reading these notes a reasonable substitute for attending lecture.
Efficiency
We discussed reducing duplicated computations. This can often be done with a local definition. We also discussed tail recursion.#lang typed/racket (require "../include/uchicago151.rkt") (: rel->abs : (Listof Number) -> (Listof Number)) (define (rel->abs l) (local {(: add-to-each : Number (Listof Number) -> (Listof Number)) (define (add-to-each n l) (cond [(empty? l) '()] [else (cons (+ n (first l)) (add-to-each n (rest l)))]))} (cond [(empty? l) '()] [else (cons (first l) (add-to-each (first l) (rel->abs (rest l))))]))) (: rel->abs2 : (Listof Number) -> (Listof Number)) (define (rel->abs2 l) (local {(: helper : Number (Listof Number) -> (Listof Number)) (define (helper n l) (cond [(empty? l) '()] [else (cons (+ n (first l)) (helper (+ n (first l)) (rest l)))]))} (helper 0 l))) (: reverse : (All (A) (Listof A) -> (Listof A))) (define (reverse l) (local {(: add-to-end : A (Listof A) -> (Listof A)) (define (add-to-end i l) (cond [(empty? l) (list i)] [else (cons (first l) (add-to-end i (rest l)))]))} (cond [(empty? l) '()] [else (add-to-end (first l) (reverse (rest l)))]))) ;[else (append (reverse (rest l)) (list (first l)))]))) (: reverse2 : (All (A) (Listof A) -> (Listof A))) (define (reverse2 l) (local {(: helper : (Listof A) (Listof A) -> (Listof A)) ; l is the input list, which we are dividing into first and rest ; m is the list we are building up (define (helper l m) (cond [(empty? l) m] [else (helper (rest l) (cons (first l) m))]))} (helper l '()))) (: append : (All (A) (Listof A) (Listof A) -> (Listof A))) (define (append l m) (cond [(empty? l) m] [else (cons (first l) (append (rest l) m))])) (: listmin : (Listof Real) -> Real) (define (listmin l) (cond [(empty? l) +inf.0] [(< (first l) (listmin (rest l))) (first l)] [else (listmin (rest l))])) (: listmin2 : (Listof Real) -> Real) (define (listmin2 l) (local {(: helper : Real (Listof Real) -> Real) (define (helper min-so-far nums-left) (cond [(empty? nums-left) min-so-far] [else (helper (min min-so-far (first nums-left)) (rest nums-left))]))} (helper +inf.0 l))) (: fact : Nonnegative-Integer -> Positive-Integer) (define (fact n) (cond [(= n 0) 1] [else (* n (fact (sub1 n)))])) (: fact2 : Nonnegative-Integer -> Positive-Integer) (define (fact2 n) (local {(: helper : Positive-Integer Nonnegative-Integer -> Positive-Integer) (define (helper product n) (cond [(= n 0) product] [else (helper (* product n) (sub1 n))]))} (helper 1 n))) ; Write sum using tail recursion (: sum : (Listof Number) -> Number) (define (sum l) (local {(: helper : Number (Listof Number) -> Number) (define (helper n l) (cond [(empty? l) n] [else (helper (+ n (first l)) (rest l))]))} (helper 0 l))) ; The version of merge sort that we did in lecture had a lot of duplicated ; computations. The take and drop functions do the same things twice: ;(define (take n l) ; (cond ; [(= n 0) '()] ; [else (cons (first l) (take (sub1 n) (rest l)))])) ;(define (drop n l) ; (cond ; [(= n 0) l] ; [else (drop (sub1 n) (rest l))])) ; Rewrite merge sort so that it only walks down the list one time to split the ; list into two pieces. (define-struct (Pair A B) ([a : A] [b : B])) (: msort : (All (A) (A A -> Boolean) (Listof A) -> (Listof A))) (define (msort f l) (cond [(or (empty? l) (empty? (rest l))) l] [else (local {(: half-len : Nonnegative-Integer) (define half-len (quotient (length l) 2)) (: split : Nonnegative-Integer (Listof A) (Listof A) -> (Pair (Listof A) (Listof A))) (define (split n l m) (cond [(= n 0) (Pair l m)] [else (split (sub1 n) (rest l) (cons (first l) m))])) (: splits : (Pair (Listof A) (Listof A))) (define splits (split half-len l '())) (: merge : (Listof A) (Listof A) -> (Listof A)) (define (merge l m) (cond [(empty? l) m] [(empty? m) l] [(f (first l) (first m)) (cons (first l) (merge (rest l) m))] [else (cons (first m) (merge l (rest m)))]))} (merge (msort f (Pair-a splits)) (msort f (Pair-b splits))))])) (msort < (build-list 30 (λ ([n : Integer]) (random 100)))) ; The version of quick sort that we did in lecture also had duplicated computations ; Rewrite it so that, instead of calling filter twice, it only processes the list ; once. Similar to the merge sort problem above, write a helper function ; that will output a pair of lists (: qsort : (All (A) (A A -> Boolean) (Listof A) -> (Listof A))) (define (qsort f l) (cond [(empty? l) l] [else (local {(: pivot : A) (define pivot (first l)) (: split : (Listof A) (Listof A) (Listof A) -> (Pair (Listof A) (Listof A))) (define (split l smaller greater) (cond [(empty? l) (Pair smaller greater)] [(f (first l) pivot) (split (rest l) (cons (first l) smaller) greater)] [else (split (rest l) smaller (cons (first l) greater))])) (: splits : (Pair (Listof A) (Listof A))) (define splits (split (rest l) '() '()))} (append (qsort f (Pair-a splits)) (cons pivot (qsort f (Pair-b splits)))))])) (qsort >= (build-list 30 (λ ([n : Integer]) (random 100))))