| Update: the lab2 git repo is now posted and ready to be forked/cloned. |
As with lab1, the resulting code must under steel bank common lisp (sbcl) on the cubs/pups/tux.
The commands to obtain/run the command are as per lab1 (though of course the names are all lab2/test2 this time instead of lab1/test1).
Marking: as per lab1, the lab will be marked out of 18, with 14 of the marks for the implementation in lab2.cl and 4 marks for the testing you provide through test2.cl.
Lab 2 requirements
For lab 2, you are to finish implementing the let-over-lambda construct started in lab2.cl and test/debug it using your test1.cl script. (Once again I'll be running my own test2.cl script as well as your own.)
As per the discussions in lectures, the let-over(-labels-over)-lambda construction allows us to build an object-like system for a data structure: the bldRanker function takes a collection of filenames, loads their contents, and returns a function that allows the user to repeatedly run commands against the loaded data.
As with lab1, each file is expected to have a list of ranking pairs for items in a particular category, and we'll use the filename as the 'name' of the category.
The initial call to bldRanker to load a set of files would thus look like:
(defvar testDisp (bldRanker "files/foods" "files/movies"))
Subsequently, to use/manipulate the dataset associated with the testDisp dispatcher we would use funcall on testDisp (since it holds the lambda function returned from bldRanker), passing it the specific command we want to apply and any required arguments.
The section below provides sample calls to the command set your bldRanker lambda function must support, along with comments describing the required behaviour and/or return value.
; bldRanker returns a valid lambda dispatcher regardless of the nature of its passed parameters
; it should read and store a rankings list for each file
; (expecting the file formats to be the same as lab1)
; using empty lists for invalid files and files containing no valid rankings
; as with lab1, even if an (item rank) pair is invalid it should still continue
; reading/loading any further pairs in the list
(defvar testDisp (bldRanker "files/foods" "files/movies"))
; the HELP command requires no extra arguments,
; and the dispatcher should display onscreen a list of the supported
; commands with a short (one-line) description of each
(funcall testDisp 'HELP)
; the CATS command requires no extra arguments,
; and the dispatcher should return a list of the loaded filenames,
; e.g. ("files/foods" "files/movies")
(defvar categories (funcall testDisp 'CATS))
; the CRANKS command requires the category (filename) as its argument,
; and the dispatcher should return the rankings list for the specified category,
; e.g. (("Some dumb movie" 20) ("Another one" 704))
; it should display an error message and return nil if the category is invalid
(defvar movies (funcall testDisp 'CRANKS "files/movies"))
; the IRANK command expects the category and item name as its two arguments (in that order),
; and the dispatcher should return the rank of the specified item, e.g. 970
; it should display an error message and return nil if either argument is invalid
(defvar irank (funcall testDisp 'IRANK "somefilename" "someitemname"))
; the MATCH command expects three arguments: a category name then two item names
; it then runs a head-to-head match between the two items as follows
; it should check the category and item names,
; and if any of them are invalid it should display an error message and return nil
; otherwise it proceeds as follows:
; (***BE CAREFUL TO ADHERE TO THIS FOR THE SAKE OF MY TESTING OF YOUR CODE***)
; 1) it prompts the user to pick between the two items,
; displaying their names and asking them to respond with F, S, or D
; where D means draw, F means first item wins, S means second item wins
; if given invalid input it should provide an error message and repeat until valid
; 2) it uses the lab1 scoring/calculation rules to determine new rankings for the
two items, and updates them in the stored category list
; 3) it then returns a list of the two updated pairs, e.g.
; (("Poke" pokesNewRank) ("Burger" burgersNewRank))
(defvar matchRes (funcall testDisp 'MATCH "files/foods" "Poke" "Burger"))
; the RNDMATCH command expects one argument, a category name,
; and it should select two (different) random items from within that category
; and run MATCH on those two items, returning the result
; it should display an error message and return nil if the category is invalid
; or contains fewer than two items
(setf matchRes (funcall testDisp 'RNDMATCH "files/foods"))
; the ADDITEM command expects a category, item name, and item rank as its three arguments
; and should add the (item rank) pair to the specified category list
; and return the added pair
; it displays an error message and returns nil if any of the parameters are invalid
(defvar addRes (funcall testDisp 'ADDITEM "files/foods" "Ramen" 1650))
; the RMITEM command expects a category and item name as its two arguments
; and it should remove the pair from the specified category list
; it displays an error message and returns nil if any of the parameters are invalid,
; otherwise it returns true (t)
(defvar rmRes (funcall testDisp 'RMITEM "files/foods" "Beets"))
|
Implementation restrictions
The let-over-lambda construct is inherently 'impure' functional programming, so you are not expected to attemp to achieve functional purity in the implementation.
However, you are required to adhere to the let-over-lambda construct style, and your bldRanker is not permitted to make use of any global functions, variables, or constants (other than the one used for the random number generator).
You must support the dispatcher commands listed above, in the forms shown above, but are free to add additional commands if desired.
An initial framerwork is provided below (and in the initial lab2.cl file in your repo), with working functionality for the HELP command and checking for invalid (unsupported) commands.
As mentioned in the MATCH discussion above, do be careful to adhere to the specified argument orders, return values, expected input values, and sequences of events for the various commands. My testing of your code will include a variety of drivers and evaluators that expect (require) those rules be followed.
(defun bldRanker (&rest fnames )
(let ( ; local variables, currently none
) ; end of local variables
(labels ( ; local functions
(help ()
"provide list of available commands"
(format t "~%Supported commands:~%")
(format t " 'HELP: prints this help menu~%~%"))
(invalidCommand (cmd)
"let user know they have supplied an unsupported command and give help"
(format t "Error: invalid cmd supplied: ~A~%" cmd)
(help))
) ; end of local functions
; initialization section
(setf *random-state* (make-random-state t)) ; seed the random number generator
; end of initialization section
(lambda (cmd &optional (arg1 nil) (arg2 nil) (arg3 nil))
(cond
; user assistance commands
((equalp cmd 'HELP) (help)) ; display list of supported commands
(t (invalidCommand cmd)) ; let user know the command was invalid, then display help
)))))
|