;; 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 passing-functions) (read-case-sensitive #t) (teachpacks ()) (htdp-settings #(#t constructor repeating-decimal #f #t none #f () #f))) ;;;; In the video-game 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 (list 3 7 1) <) ; call `sort` alphabetically: (sort (list "Stephan" "Tre" "Meseret") string>=?) ; sort by length: (define (string-shorter-than? s1 s2) (< (string-length s1) (string-length s2))) (sort (list "Stephan" "Tre" "Meseret") string-shorter-than?) ; sort by length, but don't bother naming the function as a separate define: (sort (list "Stephan" "Tre" "Meseret") (λ (s1 s2) (> (string-length s1) (string-length s2)))) ; Truth: there is only one "real" version of `define`; ; it associates a name with a value. (define speed-limit 85) ;(define (is-over-limit? n) ...) ;; turns into: ;(define is-over-limit? (lambda (n) ...)) #| HANDY EDITOR TIP for DrRacket: alt-backslash inserts the character "λ" (mnemonic: the backslash is reminiscent of the "backbone" of the lambda-character) If you're really fancy: esc-shift-l inserts the template "(λ () )"with your cursor in the center Many other special-characters can be entered if you know/guess their latex name: \alpha CTRL-\ to insert the char "α" \rightarrow CTRL-\ to insert the char "→" ) While we're digressing, some more useful facts: - ESC-RIGHT and ESC-LEFT to move an entire expression at a time. - Use "#;" to comment out exactly one expression. V.handy. |# ;;;;; 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 = List.of( "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, pre-Java-8 (and, what Java8 compiles down to): // 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)); } } class BandComparer2 implement Comparator { public int compare(String s1, String s2) { return s1.length() < s2.length(); } } // Now, use that class: bands = Arrays.asList( "Modest Mouse", "The Police", "The Cars", "The Black Keys", "The The" ); Collections.sort(bands, new BandComparer2() ); 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() ); In Java 8: Yay, it's now easy to create the code that you pass to the function: Collection.sort( bands, (s1,s2)->removePrefixIf("The ",s1).compareTo(removePrefixIf("The ",s2)) ); If you want to write your own functions that *receive* a lambda as input, you still need to declare an interface with one method inside it. Fortunately, many run-of-the-mill such interfaces are provided for you in java.util.function : https://docs.oracle.com/javase/9/docs/api/java/util/function/Function.html https://docs.oracle.com/javase/9/docs/api/java/util/function/Predicate.html List myFilter( List data, Predicate keepP ) { if (data.isEmpty()) { return new LinkedList(); } else { if (keepP.test( data.get(0) )) { List resultRecur = myFilter( data.sublist(1,data.size()), keepP ).clone(); resultRecur.add(data.get(0),0); // push to front of list return resultRecur; } else { return myFilter( data.sublist(1,data.size()), keepP ); } } } |# #| 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.) |# ;;;;;;;;;; How to call a function that is passed into a function ;;;;;;;;; ;; write `call-twice` which takes in a function f and an input n, and return f(f(5)): (check-expect (call-twice sqrt 81) 3) ; (sqrt 81) = 9, and then (sqrt 9) = 3 (check-expect (call-twice add1 7) 9) (check-expect (call-twice (lambda (str) (string-append str "!")) "hey") "hey!!") (define (collatz n) (if (even? n) (/ n 2) (+ (* 3 n) 1))) (check-expect (call-twice collatz 6) 10) ;(collatz 6) = 3, and (collatz 3) = 10. ; call-twice : (α -> α), α -> α ; (define (call-twice f n) (f (f n))) ;;;;;;;;;;;;;;;; Signatures (require 2htdp/image) (check-expect (inset circle 60 'blue 'red) (overlay (circle 20 'solid 'red) (circle 60 'solid 'blue))) ; One more example: (check-expect (inset square 60 'blue 'red) (overlay (square 20 'solid 'red) (square 60 'solid 'blue))) ; (non-negative-real, mode?, image-color? -> image?), non-negative-real, image-color?, image-color? -> image? (define (inset shape-maker sz bg fg) (overlay (shape-maker (/ sz 3) 'solid fg) (shape-maker sz 'solid bg)))