CSCI 330 Lab 1: Lisp fundamentals

In this lab you will edit and submit lisp code that runs under steel bank common lisp (sbcl) on the cubs/pups, with an emphasis on typical lisp type checking, control flow, parameter passing, i/o, recursion, and lists. The goal is to gain basic familiarity with lisp syntax and structure, then we'll rapidly get into more interesting features and functionality in future labs.

You'll have two weeks to do the lab (including the lab sessions on Jan. 15th and any time after the quiz on the 22nd), and it will be due at noon on Jan. 29th.

The exercise involves forking/cloning a git repository, editing the lab1.cl and selftest.cl files, and then submitting your updated repository.

Instructions are given below and will be discussed in the lab sessions, along with a basic intro to working with lisp/sbcl.

In this first lab we include a review of the basic git process for obtaining/submitting labs, for later labs we'll simply dive straight into the actual lab requirements.


Git submission system

For this lab (and all CSCI 330 labs this term) you will be using an adaptation of the git version control system to get the starting lab material, to track changes as you make them, and ultimately to submit your completed lab.

You must use this system to obtain and submit your work - no other submission mechanism will be accepted.

NOTE: if you have taken csci330 previously then it would be advisable to archive your old csci330 directory at the start of the term, e.g.
mv csci330 old330

The basic instructions to obtain your lab1 are shown below (issued from cubs/pups).
WARNING: this will not work until the instructor has posted the repository, which might not happen until the lab session begins: check with your instructor before proceeding.

    mkdir -p csci330
    ssh csci fork csci330/lab1 csci330/$USER/lab1
    cd csci330
    git clone csci:csci330/$USER/lab1
    cd lab1

Working on and submitting your lab

After your git fork/clone steps, in your lab1 directory you should find files named lab1script.cl and lab1fun.cl, which will be the files you are ultimately graded on for the lab.

Edit the files with whatever text editor you prefer, and whenever you are ready to submit the lab use the following commands (from within your lab1 repository):
   git add lab1.cl
   git add selftest.cl
   git commit -m "whatever your message is"
   git push
(You can do the add/commit/push cycle as often as desired when submitting the lab.)


Lab 1 objectives

Lab 1 is meant to give you practice with the fundamentals of programming in lisp:


Lab 1 requirements

For lab 1, you are to finish implementing the functions in the provided lab1.cl file, and test/debug them using your selftest.cl script. (When I mark your lab1.cl code I'll be using my own test script that loads lab1.cl.)

The functions themselves are discussed in detail in the lab1.cl file (copied below), but in general they involve checking course requirements (such as would be used for degree completion checks or pre-requisite checks), basic error checking, and GPA calculations.

With respect to the lab1.cl file and its functions, you must follow these additional restrictions:

lab1.cl: the key functions you are to implement

Skeletal versions of the functions have been set up in the lab1.cl file, you'll be completing the bodies of each of the functions.

Do not alter the names of the functions or their parameter lists.

The specifications for the functions are provided in the comments in lab1.cl, and are repeated below.


; valid strings for letter grades
(defconstant VALIDLETGRADES
   '("A+" "A" "A-" "B+" "B" "B-" "C+" "C" "C-" "D" "F" "INC" "CR" "TRF" "WDR" "UW" "CS" "NP"))

; (validGrade G)
; --------------
; return t if G represents a valid grade for a course
; otherwise display an error message identifying the specific
;    nature of the error and return nil
;
; a valid grade is a list with four parts as follows:
;   1. a string representing the department/area, e.g. "CSCI"
;   2. an integer in the range 100 to 499 representing the course number, e.g. 330
;   3. an integer in the range 0 to 5 representing the credit weight of the course, e.g. 3
;   4. a string representing a letter grade for the course, e.g. B+
;
; the function should assume that a global constant named VALIDAREAS is available,
;    containing a list of all the strings that constitute valid departments/areas
; (such a constant would have been defined in whichever file loaded lab1.cl)
;
; e.g.  ("MATH" 222 3 "C") would be valid (assuming "MATH" is in VALIDAREAS)
;       ("CSCI" 901 3 "C+") would not (invalid course number)
;       ("CSCI" 161 4) would not (missing grade)
;    etc
(defun validGrade (G)
   )

; (letterToGPA letterGrade)
; -------------------------
; given a letter grade as a string, e.g. "A+",
; return the numeric equivalent using the following scale:
;   A+ 4.33    B+ 3.33    C+ 2.33    D  1.00
;   A  4.00    B  3.00    C  2.00    F  0.00
;   A- 3.67    B- 2.67    C- 1.67    UW 0.00
;
; The following letter grade strings return a value of nil but are
; not considered errors:
;    INC, WDR, NP, CS, CR, TRF, AUD
;
; Any other letter grade value causes a return value of nil after
; displaying an error message if the parameter is not a valid letter grade
(defun letterToGPA (letterGrade)
   )

; (gpaCalc Grades)
; ----------------
; gpaCalc computes and returns the GPA for a given list of grades, e.g.
;    (gpaCalc '(("CSCI" 161 4 "A-") ("ENGL" 204 3 "B") ("MATH" 123 3 "B+")))
;    would return a gpa average of 3.33 from the three courses
;
; GPA calculation:
;    each valid grade contributes the course credit value * the gpa mark
;       to the overall gpa calculation
;    the average gpa is then the total of all the contributions
;       divided by the total credits granted
;
;    e.g.
;            "MATH" 123 3 "B+": gpa of 3.33 * 3 credits = 9.99 contrib
;            "CSCI" 161 4 "A-": gpa of 3.67 * 4 credits = 14.68 contrib
;            "ENGL" 204 3 "B":  gpa of 3.00 * 3 credits = 9.00 contrib
;       so gpa is (9.99+14.68+9.00)/10 credits = 3.367
;
;    note that
;      (1) if letterToGPA returns nil for a course then that grade
;          should not be included in the grade calculation
;      (2) if a student takes a course multiple times then they all
;          count in the gpa calculation (other than attempts eliminated by (1) above)
;
; Error handling:
;    if the Grades parameter is not a list then an error message is displayed
;       (including the value of Grades) and nil is returned,
;    otherwise the process below is followed to compute the grade
;
;    validGrades should be used to check each individual element of Grades,
;       and any invalid grades are skipped (gpaCalc itself does not end on
;       invalid individual grades and does not itself generate error messages
;       for invalid individual grades - those error messages are left to
;       the validGrades function to display)
;
;    if Grades contains no valid grades at all (e.g. it is an empty list or
;       all the entries are invalid) then a default gpa of 0 is returned
(defun gpaCalc (Grades)
   )

; (meetsReq Req Act)
; ------------------
; meetsReq checks an actual grade against a required grade,
;   returning t if the requirement is met, nil otherwise
; e.g. (meetsReq '("CSCI" 260 4 "C") '("CSCI" 260 4 "B-")) returns t (courses match, grade ok)
;      (meetsReq '("CSCI" 265 4 "C") '("CSCI" 265 4 "C-")) returns nil (right course, grade too low)
;      (meetsReq '("CSCI" 112 3 "C") '("CSCI" 115 3 "C")) returns nil (wrong course)
;
; For a requirement to be met, the first three elements must be exact matches
;    (it must be the same dept/area string, course number, and credit value)
; and the actual grade must either be "TRF" or be equal to or higher than the
; required grade.
;
; Error handling:
;    meetsReq should use validGrade to check each of the two parameters
;       and return nil if either fails, but leaves the error message display
;       to validGrade
(defun meetsReq (Req Act)
   )

; (meetsAllReqs RList AList)
; --------------------------
; RList and AList are each lists of grades: RList is the list of courses/grades
;    a student needs, and AList is the list of courses/grades they have
; meetsAllReqs checks to see if every valid requirement in RList is met by at least
;    one course from AList
;
; e.g. suppose
;   RList is '(("CSCI" 159 4 "C-") ("CSCI" 251 3 "C-") ("MATH" 123 3 "C-"))
;   AList is '(("ENGL" 204 3 "A-") ("MATH 121 3 "A") ("MATH" 123 3 "C+") ("CSCI" 251 3 "TRF"))
; then meetsAllReqs would return nil because of the missing 159 requirement
;
; meetsAllReqs displays a list of each requirement that has not been met
;    (i.e. after checking everything in AList the requirement has not been met)
;
; Error handling:
;    if RList or AList are not lists then meetsAllReqs
;       displays an error message and returns nil
;    but all other error checking/messages is left to the
;       individual calls to meetsReq
;
; Note: the courses in each list might appear in any order, do not make any assumptions
;   about courses appearing in a specific order
(defun meetsAllReqs (RList AList)
   )

Additional tips:

selftest.cl: a test script for checking/debugging your functions

The selftest.cl script is executable. It declares the needed VALIDAREAS constant, loads the lab1.cl file, then executes any desired testing.

Its intended use is as a testing (and debugging) mechanism for you to check that your lab1.cl functions actually work correctly. All testing and debugging code, including test values, belongs in this script, NOT in lab1.cl. Marks will be deducted if you including testing/debugging content in your lab1.cl file.

A few sample data values and test calls are included in the initial version of selftest.cl, but these are nowhere near complete. A copy of the initial selftest file is shown below.
#! /usr/bin/sbcl --script

; ******************************************************
; THE TEST CASES/CALLS BELOW ARE MERELY A STARTING POINT
; FOR TESTING YOUR CODE, THEY ARE NOT ADEQUATE TO ENSURE
; YOUR CODE IS ANYWHERE CLOSE TO SUFFICIENT
; ******************************************************

; define a constant identifying all the valid departments/areas for the
;    purposes of the test (this is used by the validGrade function)
(defconstant VALIDAREAS '("BIOL" "CSCI" "MATH" "ENGL"))

; ensure the lab1 functions are correctly loaded
(load "lab1.cl")
(format t "---~%")
(format t "lab1.cl functions loading complete~%")
(format t "---~%")

; sample values for use in the simple tests below
(defvar okReq '("CSCI" 159 4 "C"))
(defvar okGrade '("CSCI" 159 4 "C+"))
(defvar badGrade '("CSCI" 4 "C+"))
(defvar okMath '("MATH" 123 3 "C-"))
(defvar reqsList (list okReq okMath))
(defvar actualList (list okGrade okMath))
(defvar oneCourse (list okMath))

; simple checks of validGrade
(format t "Checking validity of ~A, should pass~%" okGrade)
(format t "...result: ~A~%" (validGrade okGrade))
(format t "Checking validity of ~A, should fail~%" badGrade)
(format t "...result: ~A~%" (validGrade badGrade))
(format t "---~%")

; simple checks of letterToGPA
(format t "(letterToGPA ~A) should give 3.33~%" "B+")
(format t "...result: ~A~%" (letterToGPA "B+"))
(format t "(letterToGPA ~A) should give nil~%" "foo")
(format t "...result: ~A~%" (letterToGPA "foo"))
(format t "---~%")

; simple check of meetsReq
(format t "(meetsReq ~A ~A) should pass~%" okReq okGrade)
(format t "... result: ~A~%" (meetsReq okReq okGrade))
(format t "(meetsReq ~A ~A) should fail~%" okReq badGrade)
(format t "... result: ~A~%" (meetsReq okReq badGrade))
(format t "---~%")

; simple checking of meetsAllReqs
(format t "(meetsAllReqs ~A ~A) should pass~%" reqsList actualList)
(format t "... result: ~A~%" (meetsAllReqs reqsList actualList))
(format t "(meetsAllReqs ~A ~A) should fail~%" reqsList oneCourse)
(format t "... result: ~A~%" (meetsAllReqs reqsList oneCourse))
(format t "---~%")

; simple check of gpaCalc
(format t "(gpaCalc ~A), should give about 2.05~%" actualList)
(format t "... result: ~A~%" (gpaCalc actualList))
(format t "---~%")

Sample working self-test run

The example below shows a run of the initial selftest.cl but with the functions in lab1.cl been completed:

---
lab1.cl functions loading complete
---
Checking validity of (CSCI 159 4 C+), should pass
...result: T
Checking validity of (CSCI 4 C+), should fail
Error, invalid grade: not a list of 4 elements
...result: NIL
---
(letterToGPA B+) should give 3.33
...result: 3.33
(letterToGPA foo) should give nil
Error: invalid letter grade foo
...result: NIL
---
(meetsReq (CSCI 159 4 C) (CSCI 159 4 C+)) should pass
... result: T
(meetsReq (CSCI 159 4 C) (CSCI 4 C+)) should fail
Error, invalid grade: not a list of 4 elements
... result: NIL
---
(meetsAllReqs ((CSCI 159 4 C) (MATH 123 3 C-)) ((CSCI 159 4 C+) (MATH 123 3 C-))) should pass
... result: T
(meetsAllReqs ((CSCI 159 4 C) (MATH 123 3 C-)) ((MATH 123 3 C-))) should fail
UNMET Requirement: (CSCI 159 4 C)
... result: NIL
---
(gpaCalc ((CSCI 159 4 C+) (MATH 123 3 C-))), should give about 2.05
... result: 2.0471427
---