;; 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 lect18-passing-functions) (read-case-sensitive #t) (teachpacks ()) (htdp-settings #(#t constructor repeating-decimal #f #t none #f ()))) ;;;; In the hw03b soln, we called 'big-bang'. ;;;; (a) What is the view? The controller? The model? ;;;; (b) Hey, we're calling big-bang, and passing it a *function(?!)*. ;(sort (cons 3 (cons 7 (cons 1 empty))) <) ;(sort (list "Alecx" "Jason" "Scott" "Bucky") string>=?) ;;;;; 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"? ; Really, the 'important' part of this is just being able to compare ; two individual bands, and see which should come earlier: ; (check-expect (band<=? "ABC" "XTC") true) (check-expect (band<=? "XTC" "ABC") false) (check-expect (band<=? "M" "M") true) (check-expect (band<=? "The Cars" "Lorde") true) (check-expect (band<=? "Lorde" "The Cars") false) (check-expect (band<=? "The Black Keys" "The Cars") true) (check-expect (band<=? "The Black Keys" "The The") true) (check-expect (band<=? "The Zombies" "The The") false) (check-expect (band<=? "The" "ABC") false) (check-expect (band<=? "The" "The The") true) (check-expect (band<=? "ABC" "The") true) (check-expect (band<=? "The The" "The") true) ; remove-prefix-if : string, string -> string ; If `str` starts with `pref` (followed by strictly more), ; then remove the prefix `pref`, otherwise just return `str`. ; (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") "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" "") "") ; remove-prefix-if : string, string -> string ; Remove the `pref` from the front of `str` if it was there ; (*and* `str` had *more* stuff after the prefix). ; (define (remove-prefix-if pref str) (if (and (> (string-length str) (string-length pref)) (string=? pref (substring str 0 (string-length pref)))) (substring str (string-length pref)) str)) ; band<=? : string, string -> boolean ; Given two band-names (non-empty strings), sort them alphabetically ; but ignore any leading "The". ; (define (band<=? s1 s2) (string<=? (remove-prefix-if "The " s1) (remove-prefix-if "The " s2))) (sort (list "Modest Mouse" "The Police" "The Cars" "The Black Keys" "The The") band<=?) ;;; OR: Don't bother naming the function; use an anonymous function: ; (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)))) ; The keyword 'lambda' simply means "this next bit is a function, but ; we're not going to name it." ; In javascript they use the keyword `function`, and see below for Java 8. ; (The name stems from Alonzo Church and "the lambda calculus", in 1930s, ; a model of computation similar to the Turing Machine (but more math-based, ; and type-based.) Note that it pre-dates computers.) ; #| Java: // The interface 'Comparator' is for objects that have one method: // 'compare'. Make a whole class, to hold the specific // code we want: // class BandComparer implements Comparator { public int compare(String s1, String s2) { return removePrefixIf("The ", s1).compareTo(removePrefixIf("The ",s2)); } } // Now, use that class: bands = Arrays.asList( "Modest Mouse", "The Police", "The Cars", "The Black Keys", "The The" ); Collections.sort(bands, new BandComparer() ); System.out.println( bands.toString() ); // If we don't want the overhead of the extra class (with its own file?), // we can also use an anonymous class: // 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() ); |# ;;;; scope; binding-occurrence [the place that *defines* an id]; ;;;; "x occurs free E" ;;;; let's lifting/renaming ("alpha renaming"): ;;;; compare to function body: subst args for params (beta reduction). ;;;;;;;;;;;;;; ;;;; 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 lazy (define nats (cons 0 (map add1 nats))) (first nats) (first (rest nats)) (first (rest (rest nats))) (first (rest (rest (rest nats)))) |# #| Examples curated from: http://docs.oracle.com/javase/tutorial/java/javaOO/lambdaexpressions.html Consider the following function-call, in Java 8, which take a list of People objects, gets their email, and then prints it: processPersonsWithFunction( roster, p -> p.getGender() == Person.Sex.MALE && p.getAge() >= 18 && p.getAge() <= 25, p -> p.getEmailAddress(), email -> System.out.println(email) ); It's calling a rather-general-purpose function "give me any list, and a predicate for filtering the list, and then map an operation onto all the survivors, and use the result of that map to ". Here's how you'd write that function: public static void processPersonsWithFunction( List roster, Predicate tester, Function mapper, Consumer block) { for (Person p : roster) { if (tester.test(p)) { String data = mapper.apply(p); block.accept(data); } } } Note that (unlike racket's map,filter) these are working on *streams*, which is an advantage. (racket2 will presumably generalize map and filter directrly; for now you need to use stream-map, stream-filter, etc.) |#