;=====
; 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")))