;; 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-intermediate-lambda-reader.ss" "lang")((modname lect06b) (read-case-sensitive #t) (teachpacks ()) (htdp-settings #(#t constructor repeating-decimal #f #t none #f ()))) ;;;;;; lect06b ;;;;; Passing functions (in racket, in Java). ;;;;; Then, we'll re-visit `let` vs `let*` ...(and, see that `letrec` is different) ;;;;;;;;;;;;;;;;; ;;;;; Passing functions (in racket, in Java). (sort (list "Modest Mouse" "The Police" "The Cars" "The Black Keys" "The The") string<=?) ;; Java: #| List bands = Arrays.asList( "Modest Mouse", "The Police", "The Cars", "The Black Keys", "The The" ); Collections.sort(bands); System.out.println( bands.toString() ); |# ;; Suppose we want to sort, ignoring any initial "The"? ; remove-prefix-if : string, string -> string ; If `str` begins with `pref`, then return the remainder of `str`; ; otherwise return `str` itself. ; (check-expect (remove-prefix-if "abc" "abcdef") "def") (check-expect (remove-prefix-if "abc" "xyzdef") "xyzdef") (check-expect (remove-prefix-if "abc" "abcabc") "abc") (check-expect (remove-prefix-if "abc" "abc") "") (check-expect (remove-prefix-if "abc" "dabc") "dabc") (check-expect (remove-prefix-if "" "xyz") "xyz") (check-expect (remove-prefix-if "xyz" "ab") "ab") (check-expect (remove-prefix-if "xyz" "") "") ; (define (remove-prefix-if pref str) (if (string=? pref (substring str 0 (min (string-length pref) (string-length str)))) (substring str (string-length pref)) str)) ;;; DOIT: sort a list, ignoring any initial "The": (sort (list "Modest Mouse" "The Police" "The Cars" "The Black Keys" "The The") (lambda (s1 s2) (string<=? (remove-prefix-if "The " s1) (remove-prefix-if "The " s2)))) #| Java: bands = Arrays.asList( "Modest Mouse", "The Police", "The Cars", "The Black Keys", "The The" ); Collections.sort(bands, new Comparator() { public int compare(String s1, String s2) { return removePrefixIf("The ",s1).compareTo(removePrefixIf("The ",s2)); } } ); System.out.println( bands.toString() ); |# ;;;;;;;;;;;;;; ;;;; re-visit `let` vs `let*` ...(and, see that `letrec` is different) ;; let vs let* vs letrec (and local) ;; Recall one example of using a local variable -- ;; to name a sub-result of a bigger expression. ;; (Btw: cf Wikipedia 'cubic', 'quartic') ; initial version ; (define (root1-v1 a b c) (/ (+ (- b) (sqrt (- (* b b) (* 4 a c)))) (* 2 a))) ; ; expression has gotten pretty big/unwieldy ; (even w/o repeating subexpressions) ; ; use `let`: ; (define (root1-v2 a b c) (let {[discriminant (- (* b b) (* 4 a c))] } (/ (+ (- b) (sqrt discriminant)) (* 2 a)))) ; ; better ; Btw, did we *need* `let`, to get this simplification? ; No, we could have just used function-call: ; (define (root1-v3 a b c) (helper-root1-v3 a b c (- (* b b) (* 4 a c)))) (define (helper-root1-v3 a b c discriminant) (/ (+ (- b) (sqrt discriminant)) (* 2 a))) ; ; Finally: do we *need* to give a name, to the helper? ; We can make an anonymous function instead: ; (define (root1-v4 a b c) ((λ (discriminant) (/ (+ (- b) (sqrt discriminant)) (* 2 a))) (- (* b b) (* 4 a c)))) ; test-all : number number number (list-of (number number number -> number)) ; -> (list-of number) ; Given 3 numbers and a list of functions that accept three numbers, ; call each function on the the three given numbers. ; return a list of the results. ; (define (test-all a b c root1s) (cond [(empty? root1s) empty] [(cons? root1s) (cons ((first root1s) a b c) (test-all a b c (rest root1s)))])) (check-expect (test-all 1 0 -1 (list root1-v1 root1-v2 root1-v3 root1-v4)) (list 1 1 1 1)) (define a 1) (define b 2) (define c 3) (define d 4) (let {[a 101] [b (* 17 a)] [c (+ b c)]} (cons a (cons b (cons c (cons d empty))))) (+ a 7) ;;; syntax of `let`: #| (let {[id expr_rhs]... } expr_body) Semantics of `let`: (let {[id expr_rhs] ...} expr_body) evals to the result of eval'ing: ((λ (id) expr_body) expr_rhs) |# ;; lect06b: re-write this `let` using a lambda: (let {[a 101] [b (* 17 a)] [c (+ b c)]} (cons a (cons b (cons c (cons d empty))))) ;;;; DOIT ((lambda (a b c) (cons a (cons b (cons c (cons d empty))))) 101 (* 17 a) (+ b c)) ;; re-write this `let*` using a lambda(s): (let* {[a 101] [b (* 17 a)] [c (+ b c)]} (cons a (cons b (cons c (cons d empty))))) ; First re-write the outermost binding (only): ; ((λ (a) (let* {[b (* 17 a)] [c (+ b c)]} (cons a (cons b (cons c (cons d empty)))))) 101) ((λ (a) (let* {[b (* 17 a)] [c (+ b c)]} (cons a (cons b (cons c (cons d empty)))))) 101) ; let: the scope of all identifiers is ; (just) the let's body. ; let*: the scope of an identifer is ; the let's body, *and* all right-hand-sides following its definition. ; What is this, as a call to a helper function? #;(define my-even? sqrt) #;(define (my-odd? n) (cond [(zero? n) false] [(positive? n) (my-even? (sub1 n))])) #;(letrec {[my-even? (λ (n) (cond [(zero? n) true] [(positive? n) (my-odd? (sub1 n))]))] [my-odd? (λ (n) (cond [(zero? n) false] [(positive? n) (my-even? (sub1 n))]))] } (cons (my-even? 17) (cons (my-odd? 17) (cons (my-even? 16) (cons (my-odd? 16) empty))))) #;(local {(define (my-even? n) (cond [(zero? n) true] [(positive? n) (my-odd? (sub1 n))])) (define (my-odd? n) (cond [(zero? n) false] [(positive? n) (my-even? (sub1 n))]))} (cons (my-even? 17) (cons (my-odd? 17) (cons (my-even? 16) (cons (my-odd? 16) empty))))) ;; The scope of each is well-defined. ;; Cf. C: ;; void foo( int x ) { int x = x; } ;; The meaning of this changed between gcc-3.7.0 and gcc-3.7.1 (?) ;; We'd like well-defined semantics, not just English descriptions! ;; (But we'll defer that to later.) #| An example of lazy evaluation, and how wildly different it is: #lang racket (define nats (cons 0 (map add1 nats))) (first nats) (first (rest nats)) (first (rest (rest nats))) (first (rest (rest (rest nats)))) |#