;; CS101, Spring 2013 ;; Lecture #12 ;; ;; * GENERATING RANDOM NUMBERS ;; * USING A RANDOM NUMBER GENERATOR TO PRODUCE LISTS ;; * ABSTRACTING FUNCTION PATTERNS FOR GENERAL APPLICABILITY ;; * HIGHER-ORDER FUNCTIONS ;; - FILTER, MAP, APPLY, BUILD-LIST ; ; Every high-level programming language has a means to introduce ; unpredictability (also known as non-determinism) into a pro- ; gram, usually via a "random number generator". In Racket, the ; random function is used to generate random numbers between 0 ; and ONE LESS than the single numerical argument. ; ; "Random" numbers generated by a computer are formally known as ; "pseudo-random" to underscore the fact that no truly random ; activity is involved. ; ; Contract: (random pos-whole-number) -> 0...pos-whole-number-1 ; Header: (define random (lambda (x) ...)) ; Purpose: Generate a random number between 0 and (x - 1) ; NOTE THE MINUS IN THE LINE ABOVE!!! How would ; you call the random function if you wanted a ; random number between 1 and x? ; ; Ex 1: Write a function that returns the face-up value that ; results from rolling a 6-sided die. ; ; Contract: (die-roll) -> number (Note: zero-parameter function) ; HeaderL (define die-roll (lambda () ...)) ; Purpose: Generate a random number between 1 and 6 ; Pre-function tests: (check-expect (and (>= (die-roll) 1) (<= (die-roll) 6)) #t) (check-expect (or (< (die-roll) 1) (> (die-roll) 6)) #f) ; Function: (define die-roll (lambda () (add1 (random 6)))) ;NOTE: A BETTER WAY TO DEFINE THIS FUNCTION WOULD BE TO DECLARE A ; GLOBAL CONSTANT TO USE IN PLACE OF 6 ;--------------- ; Ex 2: Write a function that returns the sum of throwing 2 ; 6-sided dice. WORK ON THIS FOR THE NEXT LAB. ; ; Contract: (dice-pair-roll) -> number (Note: zero-parameter ; function) ; Header: (define dice-pair-roll (lambda () ..)) ; Purpose: Simulate the result of rolling 2 6-sided dice ; Pre-function tests: (check-expect (and (>= (dice-pair-roll) 1) (<= (dice-pair-roll) 12)) #t) (check-expect (or (< (dice-pair-roll) 1) (> (dice-pair-roll) 12)) #f) ; ; Function: (define dice-pair-roll (lambda () ;; This function uses the predefined helper function die-roll (+ (die-roll) (die-roll)))) ;;;;;;;;;;;;;;;; RANDOMLY GENERATED LISTS ;;;;;;;;;;;;;;;;;; ; Ex 3: Write a recursive function that generates a list of 100 random ; numbers, each between 1 and 200. ; ; Contract: (gen-ran-lon) -> lon ; Header: (define gen-ran-lon (lambda () ..)) ; Purpose: Generate a list of 100 random numbers, each between ; 1 and 200 ; ; Pre-function tests: It is hard to write meaningful tests when ; a function produces random results, but we could check the ; length of the list produced. (check-expect (length (gen-ran-lon)) 100) ; ; Function: (define gen-ran-lon (lambda () (local ;; Need an internal helper function to do the recursion because ;; the gen-ran-lon function has no input parameters. [(define gen-helper (lambda (n acc) (cond [(= n 0) acc] [else (gen-helper (sub1 n) (cons (add1 (random 200)) acc))])))] (gen-helper 100 empty)))) ;; Note: The numbers in this function should be declared as global constants. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ; PATTERNS OVER FUNCTIONS and HIGHER-ORDER FUNCTIONS ; ; For one of the problems on homework 4, you wrote a function that ; filters the numbers out of a list of any data type: ; ; Contract: (filter-nums lox) -> lon ; Header: (define filter-nums (lambda (lx) ...)) ; Purpose: Return a list consisting of only the numbers in lx ; Pre-function tests: (check-expect (filter-nums '(1 a 2 b 3 c 4)) '(1 2 3 4)) (check-expect (filter-nums '()) '()) (define filter-nums (lambda (lx) (cond ; base case: return empty if list is empty [(empty? lx) empty] ; recursive case 1: cons first onto call to rest ; predicate used here is number? [(number? (first lx)) (cons (first lx) (filter-nums (rest lx)))] ; recursive case 2: continue through list [else (filter-nums (rest lx))]))) ; ; It may not surprise you to note that we could use any predicate on the ; first element in recursive case 1, and construct a list of only those ; items for which the predicate is true. ; ; For example, We could write a very similar function to produce only the ; symbols from the input list. ; ; A list of symbols (los) is either: ; 1. empty, or ; 2. it's a (cons sym ls), where sym is a symbol and ls is a ; los. ; ; Contract: (filter-syms lox) -> los ; Header: (define filter-syms (lambda (lx) ...)) ; Purpose: Return a list consisting of only the symbols in lx ; Pre-function tests: (check-expect (filter-syms '(1 a 2 b 3 c 4)) '(a b c)) (check-expect (filter-syms '()) '()) (define filter-syms (lambda (lx) (cond ; base case: return empty if list is empty [(empty? lx) empty] ; recursive case 1: cons first onto call to rest ; predicate used here is number? [(symbol? (first lx)) (cons (first lx) (filter-syms (rest lx)))] ; recursive case 2: continue through list [else (filter-syms (rest lx))]))) ; ; Read sections 21, intro and parts 21.1 and 21.2 out of HtDP 1st ed. ; ; FUNCTIONS AS PARAMETERS: ; ; One feature of languages like Racket is that functions can be ; passed as arguments to other functions. Functions that con- ; sume functions are known as HIGHER-ORDER functions. To abstract ; over the filter-nums and filter-syms functions, we could write ; a single function with an extra parameter to replace the boolean ; function called in recursive case 1 of filter-nums. ; ; To represent a function parameter type in a contract, use paren- ; theses and the following conventions: ; ; - If the function parameter consumes a single input and returns ; a boolean, the parameter should be written (X -> boolean) in ; the function contract. The X means that we don't know what type ; of input the function parameter will consume, but there is only ; one input parameter to this function. ; ; - If the function parameter consumes more than one argument and ; produces some type other than a boolean, the parameter could ; be written (X Y -> Y) in the contract, meaning that the output ; is of type Y and the inputs are of type X and Y. ; ;; The following function, filter-any, abstracts the filter-nums ;; and filter-syms functions by making the predicate function a ;; parameter. ; Contract: (filter-any (X -> boolean) lox) -> lox ; The (X -> boolean) above indicates a 1-parameter function that ; produces a boolean ; Header: (define filter-nums (lambda (pred lx) ...)) ; Purpose: Return a list consisting of only the Xs in lx for which (pred X) ; is true. ; Pre-function tests: (check-expect (filter-any number? '(1 a 2 b 3 c 4)) '(1 2 3 4)) (check-expect (filter-any symbol? '(1 a 2 b 3 c 4)) '(a b c)) (check-expect (filter-any string? '()) '()) (check-expect (filter-any string? '(1 a 2 b 3 c 4)) '()) (check-expect (filter-any boolean? '(1 a 2 b 3 c 4)) '()) (define filter-any (lambda (pred lx) (cond ; base case: return empty if list is empty [(empty? lx) empty] ; recursive case 1: cons first onto call to rest. ; if (pred (first lx)) is true, (first lx) is consed onto output list ; created by calling filter-any recursively on pred and the rest of lx [(pred (first lx)) (cons (first lx) (filter-any pred (rest lx)))] ; recursive case 2: continue through list. ; since (pred (first lx)) must be false at this point, just call ; filter-any recursively on pred and the rest of lx [else (filter-any pred (rest lx))]))) ; ; The filter pattern is so common that Racket provides a higher-order ; function called FILTER that consumes a function and a list and that ; returns a list containing only the elements of the list for which the ; function is true. ; ; Filter always returns a LIST that is <= to the input list in length. ; Filter may return a smaller list or an empty list. ; (check-expect (filter number? '(1 a 2 b 3 c 4)) '(1 2 3 4)) (check-expect (filter symbol? '(1 a 2 b 3 c 4)) '(a b c)) (check-expect (filter string? '(#t "jelly" a)) '("jelly")) ;; IN-CLASS EXAMPLES: ;;Write a function that consumes a list of anything and produces only the ;;even numbers in the list. Use the filter function instead of explicit ;;recursion. ; Contract: (evens-only lox) -> lon containing only even numbers ; Header: (define evens-only (lambda (lx) ...)) ; Purpose: Return a list containing only the even numbers from lx ; Pre-function tests: (check-expect (evens-only '(a g 1 4 66 'cat 'dog 20)) '(4 66 20)) (check-expect (evens-only '(1 3 5 7 9)) '()) (check-expect (evens-only '()) '()) ; Function definition: (define evens-only (lambda (lx) (filter (lambda (x) (and (number? x) (even? x))) lx))) ;;Write a function that consumes a list of strings and a number and produces ;;only the strings with length >= num. Use the filter function instead of ;;explicit recursion. ;; A list of strings (lost) is either ;; 1. empty, ;; 2. or its a (cons str lst), where str is a string and lst is a lost ; Contract: (str-len>=num lost num) -> los containing only strings of length >= num ; Header: (define str-len>=num (lambda (los num) ...)) ; Purpose: Return a list containing only the strings of length >= num in los ; Pre-function tests: (check-expect (str-len>=num '("everybody" "loves" "somebody" "sometime") 6) '("everybody" "somebody" "sometime")) (check-expect (str-len>=num '("hello" "my" "name" "is" "Kay") 3) '("hello" "name" "Kay")) (check-expect (str-len>=num '() 4) '()) ; Function definition: (define str-len>=num (lambda (los num) (filter (lambda (x) (>= (string-length x) num)) los))) ; ; Another common pattern when we write functions over lists is to apply ; some function to each element. This function is called MAP and it ; consumes a one-parameter function and a list and produces a list of ; the same length in which each value is the result of applying the ; function to each element in the input list. ; ; For example, to square every element in a list of numbers, we could ; type the following function invocation: (map sqr '(1 2 3 4 5)) ; ; (map sqr '(1 2 3 4)) => (list (sqr 1) (sqr 2) (sqr 3) (sqr 4)) ; ; The map function always produces a LIST that is the same length as ; its input list. ; (check-expect (map sqr '(1 2 3 4 5)) '(1 4 9 16 25)) (check-expect (map add1 '(1 2 3 4 5)) '(2 3 4 5 6)) ;; IN THE FUNCTION BELOW, MAP IS USED TO SQUARE EACH NUMBER AND THEN ;; LIST RECURSION IS USED TO ADD THE RESULT OF SQUARING EACH NUMBER. ; Contract: (sum-sqrs lon) -> number ; Header: (define sum-sqrs (lambda (ln) ...)) ; Purpose: produce the sum of the square of each element in ln ; Pre-function tests: (check-expect (sum-sqrs '(1 2 3 4 5)) 55) (check-expect (sum-sqrs empty) 0) (check-expect (sum-sqrs '(1 2 1 2 1)) 11) (define sum-sqrs (λ (numlst) (local ;; first, declare a list that is the result of squaring every ;; item in numlst, using map [(define sqr-lst (map sqr numlst)) ;; second, define a function to do recursion over the squared list ;; and add all the squared elements (define sum-help (lambda (lst) (cond [(empty? lst) 0] [else (+ (first lst) (sum-help (rest lst)))])))] ;; third, call the local recursive function (sum-help sqr-lst)))) ; ; The problem with the sum-sqrs function above is that, although ; we can use the map function to take one function out of the ; recursive case, we still have to use recursion. ; ; There is a higher-order function that makes the process above ; even simpler. That function is called APPLY. The apply function ; consumes a function and a list and returns the result of applying ; the function to all the items on the list. ; ; (apply + (list 3 4 5 6 7)) => (+ 3 4 5 6 7) ; ; The apply function always produces a single element from a list. ; ; Contract: (sum-sqrs-v2 lon) -> number ; Header: (define sum-sqrs-v2 (lambda (ln) ...)) ; Purpose: produce the sum of the square of each element in ln ; Pre-function tests: (check-expect (sum-sqrs-v2 '(1 2 3 4 5)) 55) (check-expect (sum-sqrs-v2 '()) 0) (check-expect (sum-sqrs-v2 '(1 1 1 1 1)) 5) ; Function definition: (define sum-sqrs-v2 (λ (numlst) (apply + (map sqr numlst)))) ; We could also write the function above using a nameless lambda ; function as an input to map by substituting the following line for ; the last line: (apply + (map (lambda (x) (sqr x)) numlst) ; But this would be wasted coding, when we can use the sqr function ; directly using map. ; ; Map can also be applied to any number of equal-length lists as shown ; below: ; (check-expect (map + (list 1 2 3) (list 2 2 2) (list 3 3 3)) (list (+ 1 2 3)(+ 2 2 3)(+ 3 2 3))) (check-expect (map * (list 1 2) (list 4 4) (list 10 10)) (list (* 1 4 10)(* 2 4 10))) ; ; There is also a built-in function called BUILD-LIST that takes a ; number n and a one-parameter function and which returns a list of ; length n in which the function has been applied to every number from ; 0 to n-1. ; (check-expect (build-list 5 (lambda (x) x)) '(0 1 2 3 4)) (check-expect (build-list 5 add1) '(1 2 3 4 5)) ; ; Any higher-order function can also consume an unnamed function, written ; using a lambda expression like those you wrote in lecture 3 and lab 2. ;