Midterm 2 Next Week, Tuesday.
As usual the material to be handed in is marked [Hand-in].
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.
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]
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.
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.
(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.
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.
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 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.
(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.
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.
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
:foo :key
:foobar :test-not
:testTry 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].
(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.
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.