The Department of Computer Science & Engineering
cse@buffalo

CSE202: Programming in Lisp

Course
Grades
Email

Welcome

Policies
    Grades
    Inc
    Intgrty

Preface
Part I
  Chap 1
  Chap 2
  Chap 3
  XEmacs
  Chap 4
  Chap 5
  Chap 6
  Chap 7
  Chap 8
  Chap 9
Part II
  Chap 10
  Chap 11
  Chap 12
  Chap 13
  Chap 14
  Chap 15
  Chap 16
  Chap 17
  Chap 18
  Chap 19
  Chap 20
  Chap 21
  Chap 22
  Chap 23
Part III
  Chap 24
  Chap 25
  Chap 26
  Chap 27
  Chap 28
  Chap 29
  Chap 30
  Chap 31
  Chap 32
CHAPTER 20: FUNCTIONS WITH ARBITRARY NUMBERS OF ARGUMENTS
Notes
  1. Read Chapter 20.

    You already saw one way of defining a function so that it can be called with different numbers of variables. That was by using the &optional keyword, as described in the notes for Chapter 17. The difference between using the &optional keyword and the &rest keyword, as described in Chapter 20, is that the optional parameters are each bound to one optional argument, but the rest parameter is bound to a list of an arbitrary number of optional arguments. You can have both optional parameters and rest parameters, as long as the &optional keyword comes before the &rest keyword. If so, optional parameters are bound to extra arguments as long as there are enough optional parameters, and then the rest parameter is bound to a list of the rest. Here's a demonstration:

    TEST(114): (defun foo (x &optional y z &rest w)
    	    (list x y z w))
    FOO
    
    TEST(115): (foo 1)
    (1 NIL NIL NIL)
    
    TEST(116): (foo 1 2)
    (1 2 NIL NIL)
    
    TEST(117): (foo 1 2 3)
    (1 2 3 NIL)
    
    TEST(118): (foo 1 2 3 4)
    (1 2 3 (4))
    
    TEST(119): (foo 1 2 3 4 5)
    (1 2 3 (4 5))
    
    Notice that if there is no explicit default value for optional parameters, there is an implicit default value of NIL.

  2. One problem with the example on pages 150 - 151 is that <= is a special case in that its arguments are numbers, and numbers evaluate to themselves. There is a more to deal with if we want to use this technique with functions whose arguments don't evaluate to themselves. To illustrate this, let's define a version of equal that takes an arbitrary number of arguments.

    Recall that = takes an arbitrary number of arguments, but equal doesn't. To see this, have Lisp try to evaluate

    (= 5 (+ 2 3) (- 7 2))
    
    and
    (equal 5 (+ 2 3) (- 7 2))
    
    So let's shadow equal in the ch20 package, and try to define our own ch20::equal. We can make it take at least two arguments:
    (defpackage ch20
      (:shadow equal))
    
    (in-package ch20)
    
    (defun equal (object1 object2 &rest more-objects)
      "Returns True if all the objects are lisp:equal;
       False, otherwise."
      _____
      )
    
    To start filling in the blank, let's use lisp:equal as the two-argument version of equal:
    (defun equal (object1 object2 &rest more-objects)
      "Returns True if all the objects are lisp:equal;
       False, otherwise."
      (if (lisp:equal object1 object2)
          (if more-objects
    	  _____
    	t)
        nil))
    
    (Recall that NIL is interpreted as False, and anything else as True, so we can use more-objects instead of (not (null more-objects)). You can actually test this version of equal, as long as you don't try to evaluate the blank. Try it.

    Now, if object1 and object2 are equal, and there are more objects, we want to return True if and only if object2 is equal to all the objects in more-objects. To do this, we want to evaluate a form looking like

    (equal object2 object3 ...)
    
    We might think that we could get that form by evaluating
    (cons 'equal (cons 'object2 more-objects))
    
    which is the same as
    `(equal object2 ,@more-objects)
    
    So let's try
    CH20(108): (defun equal (object1 object2 &rest more-objects)
                 "Returns True if all the objects are lisp:equal;
                  False, otherwise."
                 (if (lisp:equal object1 object2)
                     (if more-objects
      	             (eval `(equal object2 ,@more-objects))
    	           t)
                   nil))
    
    CH20(109): (equal 5 (+ 2 3) (- 7 2))
    Error: Attempt to take the value of the unbound variable `OBJECT2'.
      [condition type: UNBOUND-VARIABLE]
    
    The problem is that eval does not recognize the bindings lambda variables get when their functions are called. We will see the correct way to get around this in Chapter 22. Meanwhile, we have to create a form which is the value of object2, but quoted. Here is this version:
    CH20(110): (defun equal (object1 object2 &rest more-objects)
                 "Returns True if all the objects are lisp:equal;
                  False, otherwise."
                 (if (lisp:equal object1 object2)
                     (if more-objects
      	             (eval `(equal ',object2 ,@more-objects))
    	           t)
                   nil))
    
    CH20(111): (equal 5 (+ 2 3) (- 7 2))
    T
    
    That looks good, but we're still being fooled by the fact that numbers evaluate to themselves. Let's try
    CH20(112): (equal 'a 'a 'c 'd)
    Error: Attempt to take the value of the unbound variable `C'.
    
    The problem is that eval is now trying to evaluate the form (EQUAL 'A C D) because the value of more-objects is the list (C D). We need to quote every form in the list more-objects. To help us do this, let's define a function that is given a list, and returns the same list with every form in it quoted:
    (defun quotem (list)
      "Returns the LIST with every form in it quoted."
      (if (null list) '()
        (cons (list 'quote (first list))
    	  (quotem (rest list)))))
    
    Try that.

    Finally, here's our final version of equal:

    (defun equal (object1 object2 &rest more-objects)
      "Returns True if all the objects are lisp:equal;
       False, otherwise."
      (if (lisp:equal object1 object2)
          (if more-objects
    	  (eval `(equal ',object2 ,@(quotem more-objects)))
    	t)
        nil))
    
    and here's some tests:
    CH20(121): (equal 'a 'a 'c 'd)
    NIL
    
    CH20(122): (equal 'a 'a 'a 'a)
    T
    
    CH20(123): (equal 'a (first '(a b c)) (second '(b a c)) (third '(c b a)))
    T
    

  3. 20.6 Lisp has a function union that takes two lists of elements and returns the union of them. If neither list contains duplicate elements, the union won't either, but if one does, so will the union. In a file named ch20.cl, define ch20::union (shadowing lisp:union) to take an arbitrary number of lists (zero or more) and return the union of all of them. Your union'd list should also contain no duplicates if the argument lists don't, but may do so if any argument list does. Note also that the order of elements in the list union returns is irrelevant. You may use lisp:union in your definition of ch20::union. Submit your ch20.cl file.

Next

Copyright © 1999, 2000 by Stuart C. Shapiro. All rights reserved.

Stuart C. Shapiro <shapiro@cse.buffalo.edu>