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))))