;; 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 dist-lect07) (read-case-sensitive #t) (teachpacks ((lib "universe.ss" "teachpack" "2htdp") (lib "image.ss" "teachpack" "2htdp"))) (htdp-settings #(#t constructor repeating-decimal #f #t none #f ((lib "universe.ss" "teachpack" "2htdp") (lib "image.ss" "teachpack" "2htdp"))))) ;;;;; lect07: ;;; First, review hw05-soln.rtk ;;; Then: ;;; We have seen list-processing functions ;;; that follow the template. ;;; Many commonly-used functions are maps and filters; ;;; we defined those functions `map` and `filter` ;;; that take in function. ;;; ;;; There is one more: fold, ;;; The mother of all(*) list-processing functions. ;;; ;;; (*) Well, all *structurally recursive* functions, ;;; that is, those whose answer just depends on the fields ;;; (and a recursive call, if the field's data type is recursive). ;;; ;;; Consider: what is repeated in each the following functions?: (define (sum-all data) (cond [(empty? data) 0] [(cons? data) (+ (first data) (sum-all (rest data)))])) (define (my-length data) (cond [(empty? data) 0] [(cons? data) (add1 (my-length (rest data)))])) (define (remove99s data) (cond [(empty? data) empty] [(cons? data) (if (= 99 (first data)) (remove99s (rest data)) (cons (first data) (remove99s (rest data))))])) (define (contains99? data) (cond [(empty? data) false] [(cons? data) (or (= 99 (first data)) (contains99? (rest data)))])) (define thresh 17) (define (count-bigs nums) (cond [(empty? nums) 0] [(cons? nums) (+ (if (> (first nums) thresh) 1 0) (count-bigs (rest nums)))])) ; similarly, reverse, etc. ;;; We'll do what we always do when we have repeated code: ;;; We'll factor out the common parts into a function, ;;; and pass in (as an argument) the little bits that differ: ;; We hate writing all the above, because so much ;; of it is rote, repeated boilerplate: ;; the natural list-processing template: #;(define (f data) (cond [(empty? data) ...] [(cons? data) (... (first data) (f (rest data)))])) ; sum a list: (check-expect (my-fold + 0 (list 1 9 2 8 3 7)) 30) ; N.B. Another (long-winded) way of saying "+" ; is "(λ (f rr) (+ f rr))" ; SELF-CHECK (fill in the ...s): ; length of a list: (check-expect (my-fold (λ (f rr) (add1 rr)) 0 (list 'a 'b 'c 'd)) 4) ; remove99s from list: (check-expect (my-fold (λ (f rr) (if (= 99 f) rr (cons f rr))) empty (list 23 99 17 99 99 5 99 3 99)) (list 23 17 5 3)) ; SELF-CHECK (fill in the ...s): ; Count the items bigger than thresh: #;(check-expect (my-fold ... ... (list 23 99 17 99 99 5 99 3 99)) 6) ;;; Challenge: write a loop using my-fold, that does 'contains99?'. ;;; Discuss the difference in how the my-fold version runs ;;; differently from the version we defined above. ; my-fold : (alpha beta -> beta) beta (list-of alpha) -> beta ; Given `combiner`, a *rule* for combining the first item of a list ; with the *result* of the recursive call, ; along with the base case and the list to process, ; process that list as instructed: ; (define (my-fold combiner base-case data) (cond [(empty? data) base-case] [(cons? data) (combiner (first data) (my-fold combiner base-case (rest data)))])) #| So: map,filter,fold (and the safe-for-space foldl) replace 99% of all my loops. (N.B. the full racket lang extends map and fold to all collections.) But to use them effectively, you need to be able to easily create the functions you want to pass in to map, etc. - higher-order functions (functions that return functions): (filter even? data) (filter positive? data) ; How to get all the even, positive numbers? ; One way would be to make two passes: ??? ; But this won't work if replace 'and' with 'or' (at least not easily). ; Another way (and one that generalizes better) is to make a `positive-even?`: (filter (λ (n) (and (even? n) (positive? n))) data) In fact, we find ourselves doing this a lot: combining two predicates with `and`: e.g. (filter (λ (astr) (or (too-small? astr) (off-screen? astr)) all-astrs) We'd like something shorter. Why won't this work?: (filter (and even? positive?) data) (filter (or too-small? off-screen?) all-astrs) What would you need to write instead of each of these? Can you generalize this to an "and for functions", called, say, `and/f`? (filter (and/f even? positive?) data) ;That is: (and/f even? positive?) = (λ (n) (and (even? n) (positive? n))) |# ; and/f : (alpha->boolean), (alpha->boolean) -> (alpha->boolean) ; (define (and/f f1 f2) (λ (n) (and (f1 n) (f2 n)))) #| hw problem: Write "non" Example: (non positive?) returns a function that takes in a number, and returns true iff(*) it's non-positive. (*) iff = "if and only if" ; What is `non`s signature? ; `non` is also very handy with filters. ;;; Self-check: ;;; 'compose' is handy: ;;; example: (compose string-length ;;; If interested in more: read about ;;; andmap ;;; apply (apply + (map string-length (list "hi" "bye" "etc"))) = 8 ;;; Write filter, using fold ;;; - Curried functions Suppose I have a list of strings, and want to add a prefix to each of them: (map (λ (nm) (string-append "Ms. " nm)) names) (map (λ (nm) (string-append "Name: " nm)) names) (map (λ (nm) (string-append ">>> " nm)) names) (map (λ (nm) (string-append "Dear Sir/Madam " nm)) names) ; I'd like to abstract away the repeated code: (map (make-prepender "Ms. ") names) (map (make-prepender "Name: ") names) (map (make-prepender ">>> ") names) (map (make-prepender "Dear Sir/Madam ") names) ; make-prepender : string -> (string->string) ; (define (make-prepender pref) (λ (str) (string-append pref str)) Of course, `make-prepender` doesn't *have* to have its result used by `map`; you can use the result wherever you like: ((make-prepender "equi") "lateral") (define friend-maker (make-prepender "Howdy, ")) (friend-maker "Amy") (map friend-maker names) ; You might have a gui, where the user enters the desired prefix, ; and then you pass the result of make-prepender into a bunch ; of other code for doing maps etc. Similarly, I often want to go through and adjust everybody's hw score: Rather than (map (λ (gr) (+ gr 7)) hw-scores) I'd like to say (map (make-adder 7) hw-scores) ((make-adder 7) 19) ; = 26 >>> Your task: Write make-adder. make-adder is "curried" version of +. And make-prepender is a curried version of string-append. That is: instead of a two-argument function that needs both its inputs immediately, we have a variant where we give the first argument to the curried version, getting back a function. Then later, we can give the second argument to that function. Curried versions are often handy when used w/ map, filter. [In O.O., this is related to the "builder pattern", and (when talking about constructors in particular), the "factory pattern".] (Haskell Curry, logician; in the language Haskell (named in his honor), *every* function is a function of one argument -- everything is auto-curried! (Or: if you ever call a function but don't give it enough arguments, you automatically get back a function that just needs to be handed the remaining arguments.)) Another example of currying a function (slight digression): Imagine the definite-integral of x^2 from 4 to 7. The answer is 7^3/3 - 4^3/3, some particular number. But if you left off the top integration bound (7), and just took the integral from 4 to some "b" to be determined later, you'd get back λ b . b^3/3 - 4^3/3 That's a curried version of the definite-integral (and it's awfully handy on its own!). In fact, no matter what the lower bound was, you get λ b . b^3/3 + C (where the exact C depends on the lower bound; really we're getting λb,a . b^3/3 - a^3/3 but in calc the exact value of C is unimportant, so they tend to gloss over the second input, a.) ;;; More on currying: + : number, number -> number make-adder: number -> (number -> number) What is the signature for string-append, vs make-prepender? For example, we had has-prefix? : string, string -> boolean What would you call a curried version, that takes in the first string (the prefix) and returns a function that always checks for that prefix? (filter (prefix-checker "The ") band-names) (filter (prefix-checker "A ") band-names) ex: Write truncate-to : n, string -> string ex: Write make-truncater : n -> (string -> string) For hot-shots only: Write `curry`: (curry +) = make-adder (curry has-prefix?) = prefix-checker (map ((curry +) 7) all-grades) (map ((curry string-append) "Dear ") names) Sometimes, you still need to use lambda just to switch the order of the arguments: (map ((curry string-times) 2) (list "hello" "anybody home? " "echo")) (map (λ (n) (string-times n "hello")) (list 4 7 3)) Tip: when defining new fnctions (like `has-prefix?`), you have to choose the order of the arguments. Think about which version makes the default 'curry' helpful: have the first arg be one that might be the same in a loop (like "Dear "), and the second one that might be looped over (like a list of names). The 'correct' choice usually more clear in retrospect, when you stumble across a need for a particular version. If interested in yet more: see 'cut': http://doc.racket-lang.org/srfi-std/srfi-26.html |# ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; How to write our own loop?: ; We want to write something like: ; (while-v1 string-short? "hello" append-dot) ; => "hello............" ; (suppose I'm building a table of contents, and want to right-pad a string.) (check-expect (while-v1 (lambda (ssf) (< (string-length ssf) 15)) (lambda (ssf) (string-append ssf ".")) "hello") "hello..........") (check-expect (while-v1 (lambda (s) (< (string-length s) 3)) (lambda (s) (string-append s ".")) "hello") "hello") ; while-v1 : (alpha->boolean), (alpha->alpha), alpha -> alpha ; Starting with `so-far`, as long as `(continue? so-far)` returns ; true, we update `so-far` to be `(body so-far)`. ; (define (while-v1 continue? body so-far) (if (continue? so-far) (while-v1 continue? body (body so-far)) so-far)) #| The Java equivalent loop: soFar = [initial value for so-far]; while (continue?(soFar)) { soFar = body(soFar); } |# ; OPTIONAL: ; We could also avoid having to continually re-pass ; the same continue-condition and body each time: ; (define (while-v1b continue? body init) (let {[my-loop (λ (so-far) (if (continue? so-far) (my-loop (body so-far)) so-far))]} (my-loop init))) ; (*** N.B. in full `#lang racket` you can use internal defines.) ; OPTIONAL: ; We could also combine having to define the function ; and make the initial call to it by using "named let", ; (a construct invented for exactly this sort of situation). ; (***requires `#lang racket` for named-let) ; (define (while-v1c continue? body init) (let my-loop {[so-far init]} (if (continue? so-far) (my-loop (body so-far)) so-far))) ; ; Remember that regular `let` can be re-written as a function call, ; so this variant "named-let" is actually pretty natural. ; Note that the code for while-v1 does *not* come from the design recipe! ; It is "generative recursion" -- it recurs on a new thing it just generated, ; rather than a sub-structure of its input. ; Nothing wrong with generative recursion, but notice that ; there's no longer any *guarantee* of termination. ; (If we used the design-recipe and onlyl recurred on substructures, ; we were guaranteed termination for free.) ; ; For example: will this terminate? ; http://en.wikipedia.org/wiki/Collatz_conjecture ; (define (>1? n) (> n 1)) ; is n bigger than 1? (define (collatz n) (if (even? n) (/ n 2) (+ (* 3 n) 1))) (while-v1 >1? collatz 2345) ; self-check: write a version of while, where you ; provide both an index *and* an accumulator variable. ; #;(while-v2 positive? 15 sub1 (lambda (s) (string-append s "hi")) "") ; To think about: ; What if we had three variables to update each time through the loop? ; What might a general-purpose 'while' look like? ; Can we write a 'for' loop, similar to while-v1? #;(check-expect (for 1 12 ??? "blastoff!") "11..10..9..8..7..6..5..4..3..2..1..blastoff!") #| First: How to complete the Java code?: String asf = "blastoff!"; // asf="answer so far" for (int i=1; i < 12; ++i) { asf = i + ".." + asf; } |# ; Now we can figure out the body we'd want to use for our loop: (check-expect (for 1 12 (λ (i asf) (string-append (number->string i) ".." asf)) "blastoff!") "11..10..9..8..7..6..5..4..3..2..1..blastoff!") ; Task: ; (a) What is the signature of `for`? ; (b) Now, write the code for it. ; (answers below the next block comment) #| By the way: what is Java's rationale for having both a loop 'while', and a loop 'for'? After all, either can be re-written into the other. The answer is in a `hile` loop, the control flow is scattered/commingled throughout the rest of the code; in a for-loop the control-flow logic is all grouped together in a single line, letting you focus on the loop-body entirely by itself. (However, `while` is more natural in some settings, so it's not entirely superceded by `for`.) map, filter, fold take this a step further: When I look at a for-loop, 90% of the time it's "int i=0; i < N; ++i", and I know that idiom well. But I still have to pore over the body, to see if it's doing mapping, filtering, or folding (or something else). When I see code written with map,filter (and to a lesser extent, fold) I can even more quickly tell what the code is doing, knowing those idioms. |# ; for : number number (number, alpha -> alpha) alpha -> alpha ; (define (for start stop body so-far) (if (< start stop) (for (add1 start) stop body (body start so-far)) so-far)) ; ; Again, if you really hated how we pass `limit` and `body` ; in each recursive call even though they don't change, ; you can use `let` ; to make a helper function that only takes in the two ; changing arguments, and uses `limit` and `body` from ; its closure.