;===== ; CS 111 - Week 6 Lecture 2 - 2024-10-03 (require 2htdp/image) (require 2htdp/universe) ;===== ; DATA DEFINITION ; an Anything is an expression of any type ;===== ; DATA DEFINITION ; a list is one of: ; - empty ; - (cons Anything list) ; cons for CONStruct a list ;==== TEMPLATE for a function that needs ; to "walk through" all of the elements of a ; variable-length list ; ; * AFTER writing your tests for such a function, ; (being SURE to include at least one test ; with an empty list AND at least one with ; a NON-empty list, and more if there are additional ; cases), ; * PASTE this into your function body to help ; in COMPLETING that function body: ; ; (define (list-template ... a-list ...) ; (cond ; [(empty? a-list) ...] ; [else (... (first a-list) ... ; (list-template ... (rest a-list) ...) ...)] ; ) ; ) ;===== ; NOTE: when a function CALLS itself -- ; as len does, and as the list-template template does -- ; that function is called a RECURSIVE function. ; ; * It is VITAL that, in a recursive function: ; * it includes at least ONE branch that does NOT ; call itself! (called a BASE case) ; * its recursive calls to itself are always on a ; "SMALLER" version of the original parameter, ; (such that, eventually, your repeated calls ; WILL eventually reach one of the base cases!) ;===== ; BSL Racket has a file input/output module ; named 2htdp/batch-io (require 2htdp/batch-io) ;===== ; ONE of SEVERAL functions for reading from a file ; from this module: ;===== ; signature: read-lines: string -> list ; purpose: expects the name of a file written as a ; string - either its absolute/complete name or ; its name in the current working folder -- ; has the SIDE EFFECT of trying to open and read ; the contents of that file, ; and returns the contents read as a list of strings, ; one string per line in the given file ;===== ; THE single (!) function for WRITING to file in this package: ;===== ; signature: write-file: string string -> string ; purpose: expects the name of a file written as a ; string - either its absolute/complete name or ; its name in the current working folder -- ; AND the string to write to that file, ; ; has the SIDE EFFECT of trying to write that string ; to that file (creating the file if necessary), ; ; and returns the name of the file hopefully-written-to ;----- ; FUN FACT: ; how can ONE string contains multiple "lines" of info? ; By including \n IN that string -- ; \n represents the newline character ; Example: ; "a\nb\nc\n" ; (this will display in the Interactions window with ; the \n showing, ; BUT works as a newline in the FILE you write to) "a\nb\nc\n" (write-file "sample.txt" "a\nb\nc\n") "" "AFTER the first write-file call, (read-lines \"sample.txt\") returns:" "==========" "" (read-lines "sample.txt") ;===== ; BUT NOTE: if the file alreadt exists, ; write-file will OVERWRITE any previous contents of that file! (write-file "sample.txt" "hello\n32") "" "AFTER the second write-file call, (read-lines \"sample.txt\") returns:" "==========" "" (read-lines "sample.txt") "" ;===== ; from the Week 2 Lab Exercise: ;===== ; signature: say-hi: string -> string ; purpose: expects a person's name ; and returns a personalized greeting to that person (check-expect (say-hi "Jo") "Howdy, Jo!") (check-expect (say-hi "Terry") "Howdy, Terry!") (define (say-hi name) (string-append "Howdy, " name "!") ) ;=== end of part from Week 2 Lab Exercise ;===== ; I would like to greet everyone in a list of names! ;===== ; signature: make-greet-list: list -> list ; purpose: expects a list of strings that are names, ; and returns a list of strings where each is ; a personalized greeting to that name (check-expect (make-greet-list empty) empty) (check-expect (make-greet-list (list "Larry" "Bob" "Steve" "Grace" "Tom" "Jerry")) (cons "Howdy, Larry!" (cons "Howdy, Bob!" (cons "Howdy, Steve!" (cons "Howdy, Grace!" (cons "Howdy, Tom!" (cons "Howdy, Jerry!" empty))))))) (define (make-greet-list name-list) (cond [(empty? name-list) empty] [else (cons (say-hi (first name-list)) (make-greet-list (rest name-list)))] ) ) (make-greet-list (list "Larry" "Bob" "Steve" "Grace" "Tom" "Jerry")) ;===== ; just for fun... (make-greet-list (read-lines "111-names.txt")) ;===== ; one common category of recursive functions ; are filters -- filtering out unwanted data ; (or, if you prefer, grabbing desired data) ;----- ; let's try one of these! ; ...to filter out any non-strings in a list, or ; to grab all the strings in a list ;===== ; signature: grab-strings: list -> list ; purpose: expects any list, and returns a list ; of JUST the strings that are top-level elements in ; that list (check-expect (grab-strings empty) empty) ;==== ; consider: what non-empty cases am I worried about here? ; ...a list all-strings? I hope the result has ALL of them! ; ...a list with a MIX of non-strings and strings? ; I hope I get JUST the strings; ; ...a list with NO strings? I hope I get an empty list; (check-expect (grab-strings (list "hi" "howdy" "hello")) (cons "hi" (cons "howdy" (cons "hello" empty)))) (check-expect (grab-strings (list 0 #false (list "moo"))) empty) (check-expect (grab-strings (list "a" 1 "b" "oink" #false (list "c"))) (cons "a" (cons "b" (cons "oink" empty)))) (define (grab-strings a-list) (cond [(empty? a-list) empty] ; do you see that the non-empty-list case has TWO ; subcases? ; It might start with a string, or it might not! ; ; It is perfectly fine to NEST a cond expression ; within a branch of a cond expression! [else (cond [(string? (first a-list)) (cons (first a-list) (grab-strings (rest a-list)))] [else (grab-strings (rest a-list))] ) ] ) ) (grab-strings (list "a" 1 "b" "oink" #false (list "c")))