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. 13th and 20th) and it will be due at the end of the day on Jan. 26th.

The exercise involves forking/cloning a git repository, editing the lab1fun.cl and lab1test.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.

Marking: the lab will be marked out of 21. Of that, 15 marks are for the function implementations in lab1fun.cl and the other 6 are for your testing of those functions in lab1test.cl.


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).

    mkdir -p csci330
    ssh csci fork csci330/lab1 csci330/$USER/lab1
    cd csci330
    git clone csci:csci330/$USER/lab1
    cd lab1
*note: the lab1 repo is now available (as of 3pm Jan. 8th)

Working on and submitting your lab

After your git fork/clone steps, in your lab1 directory you should find files named lab1test.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 lab1fun.cl
   git add lab1test.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 lab1fun.cl file, and test/debug them using your lab1test.cl script. (When I mark your lab1fun.cl code I'll be using my own test script that loads lab1fun.cl and tests the functions work as specified independently, as well as running the lab1test.cl script you modified.)

The functions themselves are discussed in detail in the lab1fun.cl file (copied below), but in general they involve letting the user build up a list of words, one at a time. After each new word is entered by the user, the program checks to see if it is an exact or near match with any of the existing words:

If the new word does match at least one existing word in at least one way then the program tells the user about the match found and asks if they still want to add the new word. If they choose "yes" then it adds the word to the front of the collection, otherwise it doesn't.

The user is given the opportunity to quit at the start and prior to gathering each new word, entering "yes" if they wish to quit, anything else to keep going.

Restrictions

With respect to the lab1fun.cl file and its functions, you must follow these additional restrictions (mostly aimed at enforcing a pure-ish functional approach and enforcing the use of lab1test.cl for testing/debugging):

lab1fun.cl: the key functions you are to implement

Skeletal versions of the functions have been set up in the lab1fun.cl file, you'll be completing the bodies of each of the functions (at the moment they each simply return nil).

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

The specifications for the functions are provided in the comments in lab1fun.cl, and are repeated below (along with description strings for each).

; (charInsert word c pos)
; -----------------------
; insert char c after the first pos characters in word
;   e.g. (charInsert "arh" \#g 2) would return "argh"
; displays error message and returns nil if pos < 0 or > word length or if word isn't a string
(defun charInsert (word c pos)
   "returns result of inserting character  c after the first pos characters of word"
   nil)


; (charDelete word i)
; -------------------
; delete the character in position i (0-based)
;   e.g. (charDelete "argh" 2) would return "arh"
; displays error message and returns nil if pos < 0 or >= word length or if word isn't a string
(defun charDelete (word i)
   "returns result after deleting the character in position i in word"
   nil)


; (isprefix pword tword)
; ----------------------
; return tword if pword is a prefix string for tword, nil otherwise
; displays error message and returns nil if either word is not a string
(defun isprefix (pword tword)
   "returns t iff pword is a prefix of tword"
    nil)


; (wordMatch word1 word2)
; -----------------------
; return t iff word1 and word2 are both strings and are exact matches
; displays error message and returns nil if either word is not a string
(defun wordMatch (word1 word2)
   " returns t iff the two words are exact matches"
   nil)


; (countDiffs word1 word2 i)
; --------------------------
; assuming word1 and word2 are equal-length strings, counts and returns
;    the number of positions in which their characters differ
;    in positions i through the end of the word
(defun countDiffs (word1 word2 i)
   "counts the number of characters that differ between two equal-length words"
   nil)


; (editDist1 word1 word2)
; -----------------------
; return t if word1 is exactly edit distance 1 from word2, nil otherwise
; where edits are
;    - insert a single char
;    - alter a single char
;    - remove a single char
; displays error message and returns nil if either word is not a string
(defun editDist1 (word1 word2)
   "returns t iff the edit distance between the two words is exactly one"
   nil)


; (gather collection)
; -------------------
; gathers a collection of words from the user, one at a time,
;    on each recursive call it
;       asks user if they want to quit or continue,
;            returning collection if they choose to quit
;       if they chose to continue it
;          gets their new word choice
;          checks if it is any of
;              - an exact match for an existing word in the collection
;              - a prefix match for an existing word in the collection
;              - a typo match for an existing word in the collection
;          if any of the above are true then it asks the user for confirmation
;             before adding the word to the collection
;          and recursively calls itself with the (possibly expanded) collection
; displays an error and returns nil if the originally-passed collection is not a list
(defun gather (collection)
   "recursively gathers and adds words to collection after checking if they are matches, prefixes, or typos of existing words"
   nil)

Additional tips:

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

The lab1test.cl script is executable. It loads the lab1fun.cl file and interacts with the user to carry out any desired testing.

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

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

(format t "~%--- Begin loading lab1fun.cl ---~%")
(load "lab1fun.cl")
(format t "~%--- completed loading of lab1fun.cl ---~%")

; ---------- unit testing of countDiffs function ---------------------------------

(defun testDiffs (w1 w2)
    (format t "Count of diffs between ~A and ~A: ~A~%" w1 w2 (countDiffs w1 w2 0)))

(format t "~%---BEGIN countDiffs tests ---~%")
(testDiffs "blar" "abcd")
(testDiffs "food" "fool")

; ---------- unit testing of charInsert function ---------------------------------

(defun testInsert (text ch index)
    (format t "Result of insert ~A into ~A at pos 0: ~A~%" ch text (charInsert text ch index)))

(format t "~%---BEGIN charInsert tests ---~%")
(testInsert "whatever" #\k 0)
(testInsert "whatever" #\k 4)
(testInsert "whatever" #\k 8)

; ---------- unit testing of charDelete function ---------------------------------

(defun testDelete (text index)
    (format t "Result of delete char at pos ~A from ~A: ~A~%" index text (charDelete text index)))

(format t "~%---BEGIN charDelete tests ---~%")
(testDelete "whatever" 0)
(testDelete "whatever" 4)
(testDelete "whatever" 7)

; ---------- unit testing of wordMatch function ---------------------------------

(defun testMatch (w1 w2)
    (format t "Result of comparing words ~A,~A for exact match: ~A~%" w1 w2 (wordMatch w1 w2)))

(format t "~%---BEGIN wordMatch tests ---~%")
(testMatch "blah" "blah")
(testMatch "x" "x")
(testMatch "blahg" "blah")

; ---------- unit testing of isprefix function ---------------------------------

(defun testPrefix (w1 w2)
    (format t "Result of checking if ~A is prefix of ~A: ~A~%" w1 w2 (isprefix w1 w2)))

(format t "~%---BEGIN isprefix tests ---~%")
(testPrefix "foo" "food")
(testPrefix "k" "klahd")
(testPrefix "blah" "blah")

; ---------- unit testing of editDist1 function ---------------------------------

(defun testEdit (s1 s2)
   (if (editDist1 s1 s2)
       (format t "Edit dist between ~A and ~A is exactly 1~%" s1 s2)
       (format t "Edit dist between ~A and ~A is NOT 1~%" s1 s2)))

(format t "~%---BEGIN editDist1 tests ---~%")
; single replace
(testEdit "argh" "argk")
; single insert
(testEdit "first" "firsty")
; single delete
(testEdit "blah" "lah")

; ---------- unit testing of gather function ---------------------------------

(format t "~%---BEGIN gather tests ---~%")

(format t "~%Commencing run of gather with an empty starting collection~%")
(format t "Resulting collection at end: ~A~%" (gather nil))

(format t "~%---END OF TESTING---~%~%")

Sample working self-test run

The example below shows a run of the initial lab1test.cl but where the functions in lab1fun.cl been completed:

--- Begin loading lab1fun.cl --- 
 
--- completed loading of lab1fun.cl --- 
 
---BEGIN countDiffs tests --- 
Count of diffs between blar and abcd: 4 
Count of diffs between food and fool: 1 
 
---BEGIN charInsert tests --- 
Result of insert k into whatever at pos 0: kwhatever 
Result of insert k into whatever at pos 0: whatkever 
Result of insert k into whatever at pos 0: whateverk 
 
---BEGIN charDelete tests --- 
Result of delete char at pos 0 from whatever: hatever 
Result of delete char at pos 4 from whatever: whaever 
Result of delete char at pos 7 from whatever: whateve 
 
---BEGIN wordMatch tests --- 
Result of comparing words blah,blah for exact match: T 
Result of comparing words x,x for exact match: T 
Result of comparing words blahg,blah for exact match: NIL 
 
---BEGIN isprefix tests --- 
Result of checking if foo is prefix of food: food 
Result of checking if k is prefix of klahd: klahd 
Result of checking if blah is prefix of blah: blah 
 
---BEGIN editDist1 tests --- 
Edit dist between argh and argk is exactly 1 
Edit dist between first and firsty is exactly 1 
Edit dist between blah and lah is exactly 1 
 
---BEGIN gather tests --- 
 
Commencing run of gather with an empty starting collection 

Do you wish to quit? (yes to quit, anything else to continue) 
no 
Please enter the next word for the collection 
foo 

Do you wish to quit? (yes to quit, anything else to continue) 
no 
Please enter the next word for the collection 
ick 

Do you wish to quit? (yes to quit, anything else to continue) 
no 
Please enter the next word for the collection 
food 
word food is a typo match against a word in the collection 
Enter yes to add it to the collection anyway, anything else to skip 
yes 

Do you wish to quit? (yes to quit, anything else to continue) 
no 
Please enter the next word for the collection 
icky 
word icky is a typo match against a word in the collection 
Enter yes to add it to the collection anyway, anything else to skip 
no 

Do you wish to quit? (yes to quit, anything else to continue) 
no 
Please enter the next word for the collection 
f 
word f is a prefix match against a word in the collection 
Enter yes to add it to the collection anyway, anything else to skip 
yes 

Do you wish to quit? (yes to quit, anything else to continue) 
yes 
Resulting collection at end: (f food ick foo) 
 
---END OF TESTING---