;=====
; CS 111 - Week 6 Lecture 1 - 2024-10-01

(require 2htdp/image)
(require 2htdp/universe)

;=====
; some list-related definition- and function-reminders:

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

empty

(cons "moo" empty)

(cons 13 (cons "moo" empty))

;-----
; signature: cons: Anything list -> list
; purpose: expects any expression (with a value)
;     and a list, and returns a new list
;     whose first value is that given expression
;     and the rest of whose values are those
;     of the given list

(cons "howdy" (cons 13 (cons "moo" empty)))

;=====
; DATA DEFINITION:
; a NonEmptyList is one of:
;     - (cons Anything empty)
;     - (cons Anything NonEmptyList)

;-----
; signature: first: NonEmptyList -> Anything
; purpose: expects a non-empty list, and
;    returns the first item in that list

(first (cons 13 (cons "moo" empty)))

;-----
; signature: rest: NonEmptyList -> list
; purpose: expects a non-empty list, and
;    returns a list of everything EXCEPT
;    the first item in the given list
;****************************************
; remember: rest ALWAYS returns a list!!
;*****************************************

(rest (cons 13 (cons "moo" empty)))

(rest (rest (cons 13 (cons "moo" empty))))

;-----
; signature: empty?: Anything -> boolean
; purpose: expects any expression with a value,
;    and returns whether it is an empty list

(empty? "moo")
(empty? (cons "haha" empty))
(empty? empty)

;-----
; signature: list?: Anything -> boolean
; purpose: expects any expression with a value,
;    and returns whether it is a list

(list? "moo")
(list? (cons "haha" empty))
(list? empty)

;-----
; signature: length: list -> number
; purpose: expects a list, and returns
;    the number of (top-level) items in
;    the list

(length empty)
(length (cons 13 empty))
(length (cons 1 (cons 2 empty)))

;====
; say I would like to "visit" each element in
;    an argument list, but that list could be of
;    ANY length
; let's develop a basic "template" for handling
;    this

;=====
; so: based on a list's definition, it can be
;    empty or not empty;

;=====
; THIS TEMPLATE IS NOT FINISHED YET!!
;
; (define (list-template ... a-list ...)
; (cond
;    [(empty? a-list) ...]
;    [else ...]
; )

;=====
; and if the list is NOT empty --
; we might find ourselves doing something
;    to or based on the FIRST element in that non-empty
;        list,
;    and to or based on the REST of the elements in that
;        non-empty list

;=====
; THIS TEMPLATE IS NOT FINISHED YET!!
;
; (define (list-template ... a-list ...)
;    (cond
;       [(empty? a-list) ...]
;       [else (... (first a-list) ...
;                  (rest a-list) ...)]
;    )
; )

;====
; say I decide to write my own version of
;   a function that just determines the top-level
;   length of a given list
; I am calling it len (so it ah doesn't conflict with
;   a certain built-in-function...)

;=====
; signature: len: list -> number
; purpose: expects any list, and returns the number
;   of (top-level) elements in that list

;=====
; NEEEEEED to test at least the empty list case
;    AND the non-empty-list case

(check-expect (len empty)
              0)

(check-expect (len (cons 1 (cons 2 (cons 3 (cons 4 empty)))))
              4)

(define (len a-list)
    (cond
       [(empty? a-list) 0]
       [else (+ 1
                (len (rest a-list)))]
    )
)

;=====
; hey, function len above is calling itself!!
; *   that's called a RECURSIVE function
; *   when your data is self-referential,
;     you may find that a recursive function is
;     useful for acting on that data
;
; TWO FEATURES you WANNNNNNT for a "GOOD" recursive
;    function:
; *   have at LEAST one "base" case,
;     a branch that does NOT have a recursive call in it
; *   in each branch with a recursive call,
;     make sure that recursive call has amongst its argument
;     a "smaller" version of the original argument
;     such that a "base" case must eventually be reached

;**********************
; THIS TEMPLATE IS NOW FINISHED!!
;**********************
; (define (list-template ... a-list ...)
;    (cond
;       [(empty? a-list) ...]
;       [else (... (first a-list) ...
;                  (list-template ... (rest a-list) ...) ...)]
;    )
; )

;=====
; BSL Racket has a file input/output module
;    named 2htdp/batch-io

(require 2htdp/batch-io)

;=====
; it has functions for simple file input/output

;=====
; one example:

;=====
; signature: read-lines: string -> list
; purpose: expects the name of a file written as a
;    string - eitherits 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

;=====
; NOTE: these won't work unless you have files
;     with THESE names in the same folder as this
;     .rkt file!!
; so, for posting purposes, I am commenting them out
;
;(read-lines "111lect06-1-PRE-CLASS.rkt")
;(read-lines "111lect06-1.rkt")
;
;(length (read-lines "111lect06-1-PRE-CLASS.rkt"))
;(len  (read-lines "111lect06-1-PRE-CLASS.rkt"))

;=====
; ADDING AFTER CLASS:
; but this has the side-effect of WRITING to a file,
;     creating it if it does not currently exist!

(write-file "example-file.txt"
    "Hello, how are you?\nI am fine!\nWant to go grab\n   a cuppa chai?")

;=====
; so these SHOULD work if you run this .rkt file!

(read-lines "example-file.txt")
(length (read-lines "example-file.txt"))
(len (read-lines "example-file.txt"))

;=====
; another example of a function "walking through" a list
; ... how about one that adds 1 to each element in a list of
;     numbers?

;====
; signature: add1-list: list -> list
; purpose: expects a list of numbers,
;    and returns a new list with each element of
;    the given list increased by 1

;=====
; again: be sure to include a test for an empty list
;    argument, as well as at least one for a non-empty-list
;    argument:

(check-expect (add1-list empty) empty)

(check-expect (add1-list (list 3 4 19 7))
              (list 4 5 20 8))

(define (add1-list num-list)
    (cond
       [(empty? num-list) empty]
       [else (cons (+ 1 (first num-list))
                   (add1-list (rest num-list)))]
    )
)

(add1-list (list 3 4 19 7))

(add1-list (list 1 88 9 110 40
                 6 9000 25 4 9/2))