;; The first three lines of this file were inserted by DrRacket. They record metadata ;; about the language level of this file in a form that our tools can easily process. #reader(lib "htdp-beginner-reader.ss" "lang")((modname lect22-dist) (read-case-sensitive #t) (teachpacks ()) (htdp-settings #(#t constructor repeating-decimal #f #t none #f ()))) ;;;;;;;;;;;;;;; my-max ; my-max : list-of-number -> number ; Return the largest number in `a-lon`. ; (define (my-max a-lon) (cond [(empty? a-lon) -inf.0] [(cons? a-lon) (if (> (my-max (rest a-lon)) (first a-lon)) (my-max (rest a-lon)) (first a-lon))])) (my-max (cons 1 (cons 2 empty))) (my-max (cons -18 (cons -17 (cons -16 (cons -15 (cons -14 (cons -13 (cons -12 (cons -11 (cons -10 (cons -9 (cons -8 (cons -7 (cons -6 (cons -5 (cons -4 (cons -3 (cons -2 (cons -1 empty))))))))))))))))))) ;;; The above version works, ...BUT: ;(my-max (cons -23 (cons -22 (cons -21 (cons -20 (cons -19 (cons -18 (cons -17 (cons -16 (cons -15 (cons -14 (cons -13 (cons -12 (cons -11 (cons -10 (cons -9 (cons -8 (cons -7 (cons -6 (cons -5 (cons -4 (cons -3 (cons -2 (cons -1 empty)))))))))))))))))))))))) ; What happened? We call the (recursive) function, and then call it again ; on the next line w/ the exact same arguments. ; If it takes (say) 1sec to make the recursive call on a list of length 100, ; then it takes us 2sec to give the answer for a list of length 101. ; And if somebody tries a list that 102 long, then they'll ; call the length-101 twice, taking 4sec. ; And if somebody tries a list that 103 long, then they'll ; call the length-102 twice, taking 8sec. ; ; This is exponential growth, and it's bad! ; Solution: once we've made the recursive call, just *remember* the answer, ; rather than call the function again with the exact same inputs. ; We'll use `let*` to introduce local variables: (require "student-extras.rkt") ; my-max : list-of-number -> number ; Return the largest number in `a-lon`. ; #;(define (my-max a-lon) (cond [(empty? a-lon) -inf.0] [(cons? a-lon) (let* {[max-of-rest (my-max (rest a-lon))]} (if (> max-of-rest (first a-lon)) max-of-rest (first a-lon)))])) ; The point of `let`: ; - avoid repeated calculation! ; - name a sub-part of the overall problem. ; Example: recall the quadratic formula from week 1: (define (root a b c) (/ (+ (- b) (sqrt (- (sqr b) (* 4 a c)))) (* 2 a))) ; vs: (define (root2 a b c) (let* {[discriminant (- (sqr b) (* 4 a c))] [numerator (+ (- b) (sqrt discriminant))] [denominator (* 2 a)]} (/ numerator denominator))) ;;;;; The syntax of let: #| (let {[ ]...} <-- 0 or more id/expr pairs, delimited by {,}. ) <-- the "body" of the let*. ; Semantics of let: ; Evaluate each right-hand-side expr and bind each result to the ; corresponding left-hand-side id. ; Then, eval the body expr (using the new bindings), ; and return that result as the answer to the overall `let`. ; We say the scope of the identifiers is the body of the 'let'. ; Syntax of let*: same as for let. ; Semantics of let*: same, but the scope of each identifier is the ; body-expression *and* the right-hand-sides of all later rules. ; N.B. `let*` is equivalent to nested-`let`s. ; N.B. We are being a bit vague about scope and shadowing; ; we'll introduce the terms `free` and `bound` variables to help, ; along with the formal notion of ; alpha-rewriting (and, for function-call, beta-reductions). |#