CS 2360 Fall 95 (lab 07v.03)
alex@cc.gatech.edu (Section A)
lyman@cc.gatech.edu (Section B)


Announcements

Assignments 6 and 7 are out. A composition of helpful hints should appear on the newsgroup soon... as well as any initial clarifications.

Midterm 2 Next Week, Tuesday.

As usual the material to be handed in is marked [Hand-in].


Today's Topics

The topics to be covered today are:

The lab covers several topics. You should have a better comprehension of the difference between lexical and dynamic scoping. You should also gain some experience with the loop macro for iteration.

Prerequisites

Today's Lab requires that you have completed the previous labs. Additionally, it assumes that you have seen and at least partially absorbed the material up to from Lecture 13. It also assumes that you have:

Step 1. loaded (and evaluated) the init.lisp file that you downloaded in Lab 1.

Step 2. You should download the file ~cs2360/public/labs/lab7.lisp at this time, also.


Lexical Scoping and Closures

Common Lisp using lexical scoping to determine the value of variables. The file lab7.lisp contains some lisp functions that you should evaluate and play with a bit to help you understand lexical scoping when you are dealing with first class functions.

The first set of functions use let to create a local lexical scope. Before evaluating the function lexical you should predict what answer will be printed. Next

With the second set of you will be playing with a version of Coke(TM) machine generator. You will be asked to add some functionality to this generator so that the number of cans and money collected are updated (decrease by 1 increase by 50 respectively ) as sodas are bought (conditional on there being at least one can left(i.e. the number of cans should never fall below 0. Return nil in that case).[Hand-in]


Dynamic Scoping

In the days before the arrival of Common Lisp, most Lisp implementations used Dynamic Scoping. Programs that utilize dynamic scoping are not functional. Unfortunately, a large enough body of dynamically scoped Lisp existed at Common Lisp's "birth" that dynamic scoping was included for compatibility reasons. Fortunately, it has been regulated to "second class" citizenship (i.e. it is not turned on unless you explicitly request/declare it.)

Global variables are usually declared to be dynamically scoped. In fact if you use defvar, defconstant, or defparameter to declare a variable, that variable is automagically made to dynamically scoped. [Which can lead to some weird bugs if you are not careful.]

First is a lab7.lisp is a lexical scoped version of the function. Evaluate those functions. Notice the warning message presented when you evaluate the second one. A "free" variable is one which the compiler cannot determine what scope it belongs to. Why doesn't this version work?

The next in the lab7.lisp code has an example function that uses a dynamically scoped variable. This variable is not a global variable, but it seems to act like one. See the function frap-maker in the code. Evaluate that function and then try out the following examples.

                   (frap-maker 'vanilla 'lots )
                   (frap-maker 'chocolate 'no )
Now try the following expression:

                   (drop-jimmys )
Think about what happens and why.


Binding vs. Assignment

Remember that in lisp values are bound to name and that this is different from assignment in Pascal. In the lisp file you will find two functions and some variables. define these variables. Then evaluate the functions (input directions in the file). and then reexamine the variable's values afterward. Remember to predict before looking.


The Loop Macro

introduction

Common Lisp has several looping constructs such as do, do*, dolist, and dotimes. The ANSI standard version of Common Lisp added the generalized Loop Macro (actually this was introduced in CLtL2. There is a whole chapter on the subject).

The Loop macro does not stick strictly with Lisp syntax so it tends to confuse a few people. The following are a couple of examples of what you can do with the Loop Macro. You should type in this loops and play with them a bit.

NOTE: The Loop Macro creates local variables (for iteration for example) implicitly.

[Hand-in] You will find a version of factorial with some missing args to the loop macro. Fill those in and turnin the finished function.

The examples below appear in the file too. Along with alternative versions using the older constructs.

Looping N times

The loop macro allow you to do basic iteration control. You can do "for" loops (ala Algol For loops) by using the macro in the following way.

                   (loop for x from 0 to 10
                        do   (print x )  ) 
                   (loop for x from 10 downto 0 by 2
                       do   (print x ))
The expression that follows do is executed on each iteration of the loop. The variable x is only defined within the scope of the loop. Looping Over a List

Often in lisp we like to iterate over the items in a list (ala dolist ).

                    (loop for item in `( abc cbs nbc fox upn wb )
                          do (print item ))
                    (loop for i in '( (a b ) ( c d ) ( e f) )
                          do (format t " ~S ~%" (car i )))
The second example above is a translation of a dolist from Ian's Lecture (Winter '95) notes.

Looping to Form New Lists

You can also from new lists (more like mapcar )

                   (loop for item1  in '( 1 2 3 4 5 )
                          for item2  in '( 5 4 3 2 1 )
                         collect (* item1 item2 ) )
Unlike "for" loops is most languages we can specify an arbitrary number of iteration variables which will update upon each iteration. if one should be shorter than the other then iteration stops. In the above loop, the result of each ( * item1 item2 ) expression is collected into a list. The above is roughly equivalent to the following mapcar.

                   (mapcar #'*  '( 1 2 3 4 5 ) '( 5 4 3 2 1 ) )
The result of both is a new list.

Returning Results from Loops

Often you would like to accumulate results and then return the answer. For example the dotimes construct allows you to return the value of some lisp expression at the end. The finally clause of a loop statement is executed after all the iteration is done. The return special form returns a value from a BLOCK (the loop macro will place your iteration code into a block). This said the following is the equivalent of Ian's dotimes example from lecture.

                   (loop for i from 0 to 5
                         do ( format t " ~S ~%" i )
                         finally ( return 37 ))
Alternatively, the following loop does the same thing

                   (loop for i from 0 to 5
                         do ( format t " ~S ~%" i )
                         when (= i 5 )  return 37 )
The return 37 is equivalent to (return 37) for all you who dislike the parentheses. It is only executed when the predicate expression following the when keyword is true.

Loop to do Summation

You can also sum values

                   (loop for value in `( 1 2 3 4 5 )
                             sum value into total
                             do (print total )
                             finally ( return total ) )
NOTE: the variable total is a local variable and is declared automagically. So unlike using some of the do macros we need not enclose this iteration within a let to declare some local variables. The equivalent dolist might look like

                   (let ( (total 0 ) )
                       (dolist (value '( 1 2 3 4 5) total )
                             (setf total (+ total value ))
                             (print total )))
Also, If you take the do (print total ) line out the loop won't incrementally print.

Event Loops

You can also implement event loops (loops that loop continually until some event.)

                   (loop for x from 0
                             do (format *query-io* "~& We've looped ~A times" x )
                             when (yes-or-no-p "Are you tired of looping yet?" )
                             do (progn (print "exiting event loop" )(return t )) )
The "for x from 0" clause will cause this loop to iterate forever since no upper bound is specified.

The Loop Macro vs. CLtL1

The Loop Macro facility as described above may not be available in implementations of CLISP. To install it yourself load the file ~cs2360/public/loop.lisp into your environment. You'll probably want to compile a version of this file for faster loading. This file is the same loop.lisp file that is found in the MCL 2.0:Library folder on your neighborhood Mac server.


Optional Arguments

If you've been reading Steele you might have noticed that several of the Lisp functions we commonly use have optional and/or keyword arguments. These optional arguments all you to extend the usefulness of these functions.

You will not be required to add keyword or optional arguments to any of your functions. However, on occasion their use is nice. [And from time to time you might see them used in solutions from the TAs to add extra wiz-bang features that weren't prescribed by the homework.]

Both keyword and optional arguments are optional. They are not required to be given for the function to work properly.

Optional arguments

Optional arguments are handy in creating Lisp functions that take arguments that usually default to some value. Making them optional allow the user of that function to not have to list an argument with the default value.

If no default value is specified it is assumed that the default value is NIL. The following is a somewhat contrived function.

                   (defun add-something ( x &optional (value 1 ))
                     ( + x value ) )
       
                   (add-something 3 ) ==> 4 
                   (add-something 4 5 ) ==> 9 

Keyword arguments

Keyword arguments use keywords. Keywords are self evaluating symbols and have the form of a symbol prefixed by a colon. Examples:

                   :foo           :key
                   :foobar        :test-not 
                   :test
Try typing a few of these to the Listener. Don't put a quote in front of them. Just type them in.

Keyword arguments are more flexible since they can be given in any order. For example for a function:

         (keyed-function ( x y &key key test  )   ..  key .. ... test ...  )  
Inside of the function body the symbols sans the colon are used whereever the value of the keyword argument is needed. This function may be invoked in the following ways.

         (keyed-function 1 2 :key 23 :test #'equal )
         (keyed-function 1 2 :test #'equal :key 23 )

You might want to try and write key-assoc to take a keyword argument with the following format &key (key-accessor #'first )

[ The keyword must be passed on to any recursive call. for the above keyword to pass it on would mean ( new-assoc ...... :key-accesssor key-accessor ) ]

Sometimes a useful function to use is member but you want it to find a list inside of a list of lists. In that case you want to use equal instead of the default eql. Example

         (member  '(foo bar)  '( (a b ) (foo bar ) ( 1 2 ) ) ==> NIL
          (member  '(foo bar)  '( (a b ) (foo bar ) ( 1 2 ) :test #'equal) ==>  (FOO BAR)
       
In the file lab7.lisp you will find the function new-assoc from lab 3. You are to make the third argument into an optional argument. The default value should be #'first. [Hand-in].

Arbitary number of Arguments

A function can take an arbitrary number of arguments by using the directive &rest in the lambda list. For example

         (defun my-plus ( &rest args )
            (print args )
            (apply #'+  args ) )
This function gathers all the arguments into a list and then calls the normal plus function. The &rest directive must occur AFTER the &optional and &key directives.


Epilogue

This lab has covered a variety of topics.

You may wish to practice on do,dolist,dotimes on your own.

Note: there was a post earlier in the quarter about what the "stack" looking like... it might be time to go back and reexamine that post in light of what you've learned about closures today.