Laboratory 1

Laboratory 1

This version of the lab has solutions, and omits some details about logging on and off.

Goals

This laboratory has two goals:
  1. Providing practice using the LISP environment on the AITS system
  2. Exercising some of the concepts we've been learning in class
It is being organized as an in-laboratory experience rather than a homework assignment so that students have the opportunity to experience the "social" aspect of programming work, and so that an assistant is readily available to answer questions that come up as students get up to speed on the system.



The Exercise: Getting Started

Some friends of mine are in a Girl Scout troop. As you may know, every year the Girl Scouts sell cookies in order to raise money. This particular troop had five days in which to sell cookies, and each of the six girls in the troop did her best. (One of them, Ellen, had the flu and didn't sell any at all.) The following global definition creates a symbol called +sales+ and binds to it a table showing how the girls did on each of the five days:
(defconstant +sales+
  '((ann     4 2 3 0 4)
    (betsy   1 4 0 0 0)
    (carol   0 1 2 3 4)
    (dawn    1 3 5 1 0)
    (ellen   0 0 0 0 0)
    (fran    8 1 7 8 5))
  "Data on Girl Scout cookie sales over five-day sale period")
Notice that +sales+ is a list containing six elements, and each element is itself a list containing information about one of the girls.

Two of the girls, Ann and Dawn, are sisters, and they're very competitive. So competitive that they've decided someone needs to write some LISP code to determine who sold more cookies this year. Being good programmers, they've decided to organize the task in a very general way, so that it's easy to get and compare information about any of the girls' sales. To make things easier, they've gotten their mom to write the first function they need, which takes a list of numbers and sums it up. She wrote:

(defun sum (numlist)
  "Adds up the numbers on the list"
  (if (null numlist)
      0.0
      (+ (first numlist) (sum (rest numlist)))))


The Exercise: Doing the Work

Loading the code you've been given

By this point, you should have LISP running. The first thing to do is load up the information from file lab1.lisp. In LISP (not in the Unix shell) you can do this by evaluating the following:
      (load "lab1")
This is just the same as if you'd just typed all of lab1.lisp into the LISP environment. Type
      (describe '+sales+)
to see information about that symbol.

Function: AVERAGE

Before anything else, add a comment to the top of lab1.lisp identifying yourself, such as:
      ;;;;;;;;;;;;;;;;;;;;
      ;;; My Name Here
      ;;; Ling689A Lab 1
      ;;; Today's date
      ;;;;;;;;;;;;;;;;;;;;

Now you're ready to do some programming. First, define a function called average. It should take a single argument, which you may name anything you like. That argument will be a list. The value returned by the 'average' function should be the average of the numbers on the list; that is, you're going to take the sum of the numbers on the list and divide it by how many numbers were on the list.

If you're having difficulty getting started, draw a boxes-and-arrows diagram using Touretzky's notation. What are the pieces you need? What are the inputs and outputs for each piece? How can you connect these? If you're helping someone who's stuck (this is a note for the assistant and/or any students who have easily solved this), what I'd like you to focus on first is helping them get a correct boxes-and-arrows diagram written, then on the LISP notation for the sub-pieces, and then on writing the whole function definition.

At any point in time, you can save lab1.lisp, go back to LISP, and re-execute

  (load "lab1")
This will re-load the file with the new changes. Note that you may get errors when you re-load the file. That's ok. The best thing to do is to read the error message, quit out of the debugger, and go back to the editor to fix the file. Then save your changes, go back to LISP, etc. This is called the edit-debug cycle. (Get used to it...)

If your edited LISP code works correctly, you can then test the function, e.g. by evaluating:

  (average '(1 2 3 4))
If you've done your Touretzky reading, you'll realize we're putting the quote symbol in front of the list to make sure that there's no attempt to evaluate 1 as a function, with 2, 3, and 4 as its arguments!

Solution:

(defun average (numlist)
  "Computes the average of numbers on the list"
  (/ (sum numlist) (length numlist)))

Function: SCOUT-SALES

Next, define a function called scout-sales. This function should take two arguments. The first argument will be a symbol that is the name of a scout (you can assume it's always an actual scout, i.e. someone on the list) and the second argument will always be a list in the format of +sales+. So, for example, when the function is written you'll be able to type the following into LISP:
        (scout-sales 'betsy +sales+)
      
and get back the list
        (1 4 0 0 0)
      
Notice that when you call the function in the LISP interpreter, you'll quote the BETSY symbol so it doesn't get evaluated, but you won't quote +sales+. The reasons for this are in Touretzky, Chapter 3, but for now you can just take my word for it.

Notice, if you haven't already, that the list you get back contains only numbers; that is, it doesn't have Betsy's name at the beginning of it.

Solution:

(defun scout-sales (scout table)
  "Returns a list of the 5-day sales numbers for scout"
  (cdr (assoc scout table)))

Function: SCOUT-TOTAL-SALES

Next, you should write a function called scout-total-sales. It should take the same two arguments as scout-sales, namely a symbol that is the name of a scout and the +sales+ list. The function should return the total sales for that scout; that is, the sum of their daily sales.

When you've written the function correctly, you'll be able to execute

        (scout-total-sales 'betsy +sales+)
      
and get back the value
        5.0
      

Solution:

(defun scout-total-sales (scout table)
  "Returns the sales total for scout"
  (sum (scout-sales scout table)))

Function: SCOUT-DAILY-AVERAGE

Now you're in the homestretch. The last function you'll define is scout-daily-average. Like the other functions, this one should take the same two arguments, namely a symbol that is the name of a scout and the +sales+ list. This function should return the average number of sales per day for that scout. When you've written the function correctly, you'll be able to execute
        (scout-daily-average 'betsy +sales+)
      
and get back the value
        1.0
      
since poor Betsy only managed five sales total in her five days.

Solution:

(defun scout-daily-average (scout table)
  "Returns the average sales per day for scout"
  (average (scout-sales scout table)))

Who Wins?

Having done all this, it's finally time to tell Ann and Dawn which of them had the highest sales. In lab1.lisp you'll have noticed a function called test. All you need to do is evaluate
  (test)
That is, call the function with no arguments. If everything else works, you'll get Ann and Dawn's totals and averages and it will report who sold the most boxes of cookies.

Once you've got that working, you can actually save a record of the run to a file by using the dribble function. Do the following:

      (dribble "out1")

      (test)

      (dribble)
File out1 will contain a record of everything that happened in between the two calls to dribble. This is a generally useful way to record results when turning in assignments.

Exiting LISP

Exit LISP by evaluating
      (quit)

Turning in the Lab

The easiest way to turn in the lab is by e-mail, unless for some reason you'd rather use hardcopy. To turn in by e-mail, do the following in the Unix window on the AITS machine:
      cat lab1.lisp out1 | mailx -s "Lab1" pr689a01@umd5
This will merge together (concatenate) the contents of the two files, and send the result as input to the "mailx" program, which will send it as mail to pr689a01@umd5 with "Lab1" on the subject line.