; Racket Intro Exercise Key

; Exercise #1
; Can either assign an "anonymous" lambda function to a name
(define square
  (lambda (n)
    (* n n)))

; OR, just define the function using this shortcut
(define (squareFirst theList)
  (square (car theList)))

; Exercise #2
(define (showCons el lis) (cons el lis))
(define (showAppend el lis) (append (list el) lis))
(define (showList el lis) (list el lis))

; Exercise #3
(define (half n) (/ n 2))

; Exercise #4 - no error checking!
(define theParts
  (lambda (theList)
    (display "car: ")
    (display (car theList)) ; remember to place (car theList) inside display
    (display #\newline)
    (display "cdr: ")
    (display (cdr theList))
    (display #\newline)
    (display "caar: ")
    (display (caar theList))
    (display #\newline)
    (display "cadar: ")
    (display (cadar theList))
    (display #\newline)))

; Exercise #5
(define (squareFirstIfEven theList)
  ; if first element is even, add the square of it to the rest of the list
  (if (even? (car theList))
      (cons (square (car theList)) (cdr theList))
     ; otherwise just return the 
      theList))

; could do the same thing using append and list instead of cons
(define (squareFirstIfEven2 theList)
  ; if first element is even, append the square of it to the rest of the list
  (if (even? (car theList))
      (append (list (square (car theList))) (cdr theList))
      ; otherwise just return the 
      theList))

; Exercise #6
(define (allSquares theList)
  ; when reach the end of the list, return the empty list
  (if (null? theList) '()
      ; appends the square of the 1st element to the result of calling allSquares
      ; with the rest of the list
      (append (list (square (car theList))) (allSquares (cdr theList)))))

; Exercise #7
; Option to create result by building from '() after reach end of input list
(define (evenSq theList)
  ; when reach the end of the list, return the empty list
  (if (null? theList) '()
      ; if the first element is even, append the square (same as allSquares)
      (if (even? (car theList)) (append (list (square (car theList))) (evenSq (cdr theList)))
          ; if not even, just call evenSq with the rest of the list (effectively removes
          ; any odd element from the list)
                 (evenSq (cdr theList)))))
; Exercise #8
(define evenSquares
  (lambda (input result)
    (cond 
          ; see if done with input, if so just return the result
          ((null? input) result)
          ; if first element is even do recursive call, add the square to the _back_
          ; of the list (try to swqp the order of the elements to append - what happens?)
          ((even? (car input))
           (evenSquares (cdr input) (append result (list (square (car input))))))
          ; if odd, just process rest of list (pass in result constructed so far)
          (else
            (evenSquares (cdr input) result)))))

(define evenSquaresIf
  (lambda (input result)
    (if (null? input) result
        (if (even? (car input))
            ; add square if even
           (evenSquaresIf (cdr input) (append result (list (square (car input)))))
            ; if odd, just process rest of list (pass in result constructed so far)
            (evenSquaresIf (cdr input) result)))))

; Exercise #9
; This option uses two parameters, builds result as recursive calls are made
; Result is simply returned when the end of input is reached
; If the result is null when end of input is reached, return #f, there were no even numbers
(define evenSquaresTF
  (lambda (input result)
    (cond ((and (null? input) (null? result)) #f)
          ; see if done with input
          ((null? input) result)
          ; see if car is even
          ((even? (car input))
           (evenSquaresTF (cdr input) (append result (list (square (car input))))))
          ; if odd, process rest of list
          (else
            (evenSquaresTF (cdr input) result)))))

; Exercise #10
(define userInfo
  (lambda ()
     (begin
       (display "Enter your name: ")
       (let ((name (read)))
         (display "Hello ")
         (display name)
         (display ", please enter your age: ")
         (printMessage (read))))))

(define (printMessage age)
  (if (< age 10)
      (display "howdy doody")
      (if (< age 21)
          (display "whazzup")
          (display "have a nice day"))))

; Exercise #11
(define (squareSquare x)
  (let* ((sq (square x))
         (sq2 (square sq)))
    (if (= (modulo sq2 4) 0)
        (display "yes")
        (display "no"))))
      
; Exercise #12
(define happyMoods '(happy ecstatic swell))

(define (isHappy mymood)
  (if (eq? #f (member mymood happyMoods)) #f #t))
 
; Exercise #13
(define (checkOutFirst pred lis)
  (apply pred (list (car lis))))

(define (checkOutFirstString pred lis)
  ; string->symbol converts a string such as "isHappy" to a symbol isHappy
  ; need to use eval to convert that symbol to a procedure, which is required
  ; by apply.
  (apply (eval (string->symbol pred)) (list (car lis))))

(define (checkOutAll pred lis)
  (map pred lis))

; Exercise #14
(define (countElements l)
  (cond ((null? l) 0)
        ; if the first element is a list, must count all elements in that list
        ; and add to the number of elements in the rest of the list
        ((list? (car l))
         (+ (countElements (car l))
            (countElements (cdr l))))
        (else
         (+ 1 (countElements (cdr l))))))

; Exercise #15
; if list size < whichElement, no changes made
(define (replaceElement whichElement el l newList)
  (if (null? l)
      newList
      ; use the parameter as a counter to determine which element to replace
         (if (= 1 whichElement)
            (replaceElement (- whichElement 1) el (cdr l) (append newList (list el)))
            (replaceElement (- whichElement 1) el (cdr l) (append newList (list (car l)))))))