#!/usr/bin/sbcl --script

;;; ============================================================
;;; CMPS 3500 - Activity 08 Starter
;;; FILE: test.lisp
;;; DESCRIPTION:
;;;   Small runnable introduction to Lisp and functional programming.
;;;   Demonstrates expressions, recursion, list processing, and
;;;   higher-order functions.
;;; ============================================================

(format t "Functional Programming Starter~%")
(format t "==============================~%~%")

;; A simple function definition using 'defun'.
;; Takes one parameter 'x' and computes x * x.
(defun square (x)
  "Return the square of x."
  (* x x))

;; A recursive function that sums all numbers in a list.
;; Functional programming relies heavily on recursion instead of loops!
(defun sum-list (lst)
  "Recursively return the sum of the integers in lst."
  ;; BASE CASE: If the list is empty (null), the sum is 0.
  (if (null lst)
      0
      ;; RECURSIVE CASE: Add the first element (car) to the sum of the rest (cdr).
      (+ (car lst) (sum-list (cdr lst)))))

;; A recursive function that filters a list, keeping only even numbers.
;; 'cond' is like an if-else chain. 't' is the default/fallback case.
(defun keep-even (lst)
  "Recursively return a new list containing only the even elements of lst."
  (cond
    ;; Base case: empty list returns nil (which means an empty list in Lisp)
    ((null lst) nil)
    ;; If the first element is even, attach it ('cons') to the recursive result.
    ((evenp (car lst))
     (cons (car lst) (keep-even (cdr lst))))
    ;; Otherwise (it's odd), skip the first element and keep recursing on the rest.
    (t
     (keep-even (cdr lst)))))

;; Higher-order function: accepts another function 'f' as an argument.
;; 'funcall' is used to call a function that was passed as a variable.
(defun apply-twice (f x)
  "Apply function f to x two times."
  (funcall f (funcall f x)))

;; 'let' creates local variables. Here we define 'numbers' as a list.
;; Notice the quote (') before the list, which prevents Lisp from evaluating it as a function.
(let ((numbers '(1 2 3 4 5 6)))
  (format t "numbers        => ~a~%" numbers)
  (format t "sum-list       => ~a~%" (sum-list numbers))
  (format t "keep-even      => ~a~%" (keep-even numbers))
  
  ;; 'mapcar' applies a function to every item in a list, returning a new list.
  ;; The #' tells Lisp to grab "the function named..." rather than a variable.
  (format t "mapcar square  => ~a~%" (mapcar #'square numbers))
  (format t "apply-twice    => ~a~%" (apply-twice #'square 2)))

;; Example of reading Lisp data exactly as an s-expression from a file
(format t "~%Reading data from sample.txt...~%")
(with-open-file (stream "sample.txt" :direction :input :if-does-not-exist nil)
  (if stream
      (let ((file-data (read stream nil)))
        (format t "Read from file => ~a~%" file-data)
        (format t "Sum of file data => ~a~%" (sum-list file-data)))
      (format t "Could not open sample.txt.~%")))

(format t "~%Done.~%")
