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 17: RECURSION ON LISTS, PART 2---SYNTHESIS
Corrections
  1. page 121, line -13: Change ((+ 3 5) - 6 * 8)) to ((+ 3 5) - 6 * 8)

  2. page 121, line -7: Change combine-expression to enclose-expression

  3. Page 297, lines 1 - 11: Change
    
    17.34 (defun enclose-expression (expr)
          "EXPR is a list representing an arithmetic
           expression (using only the operators + and -)
           in normal infix notation.
           Returns a list whose one member is EXPR
           transformed into Cambridge Prefix Notation."
          (check-type expr list)
          (cond ((< (length expr) 3) expr)
                (t (combine-expr
                     (second expr) (first expr)
                     (enclose-expression (nthcdr 2 expr))))))
    
    to
    
    17.34 (defun enclose-expression (expr)
            "EXPR is a list representing an arithmetic
             expression (using only the operators + and -)
             in normal infix notation.
             Returns a list whose one member is EXPR transformed
             into Cambridge Prefix Notation."
            (check-type expr list)
            (cond ((< (length expr) 3) expr)
                  (t (enclose-expression 
                      (combine-expr
                       (second expr) (first expr) (nthcdr 2 expr))))))
    

Notes
  1. Read Chapter 17. This chapter is the heart of the book and the course. When you have succeeded in doing the exercises through Chapter 17, you will have earned a grade of C.

  2. Exercises 17.1 through 17.8 are concerned with the identity of lists as objects. They use the function eql as the basic test of identity. Two Lisp forms form1 and form2 may evaluate to the identical Lisp object, in which case, (eql form1 form2) will evaluate to True. If they don't evaluate to the identically same object, (eql form1 form2) will evaluate to False (that is, NIL). It may be that form1 and form2 evaluate to lists that have the same members in the same order, and yet are not identical, in which case (eql form1 form2) will evaluate to False, even though (equal form1 form2) will evaluate to True, because equal is more concerned with appearing the same than actually being identical. One reason this is important is that lists can have lists as members. If two members of a list are eql, that object is stored only once, but if two members of a list are only equal lists, twice as much storage is used. Another reason this is important is that many Lisp functions use eql as the default equality tester, and you might be surprised at the behavior of these functions if you didn't realize the difference between eql objects and equal objects.

    These exercises also illustrate the fact that, given a list, some functions return a list that is eql to the argument list, or eql to some sublist of it, while other functions copy all or part of their argument lists. In particular, append takes two lists, l1 and l2, and returns one list that includes a copy of l1, but incorporates l2 in it uncopied. That is, some sublist of (append l1 l2) is eql to l2, but no sublist of (append l1 l2) is eql to l1.

    What you might do even before doing 17.1 is compare Lisp's value of

    (eql '(a b c) '(a b c))
    to its value of
    (equal '(a b c) '(a b c))

    You'll need a list copying function for Exercise 17.2. Either create one by doing Exercise 17.1, or just use copy-list

    Either use the * technique as specified in these exercises, or define functions to use. For example, you could do 17.2 by doing

    CH17(84): (defun eql-copy (lst)
    	    "Returns T if a list is eql to a copy of itself;
                 NIL otherwise."
    	    (eql lst (copy-list lst)))
    EQL-COPY
    CH17(85): (eql-copy '(a b c))
    

  3. Notice that there two ways of shadowing a symbol. If you have already defined a package, and are interacting with the Lisp listener in that package, and you want to shadow a symbol, just evaluate (shadow 'symbol). For example,
    USER(82): (defpackage ch17)
    #<The CH17 package>
    USER(83): ...
    USER(99): (shadow 'identity)
    
    But, if you are defining a package and know symbols you want to shadow, or if you have a defpackage form in a file, you should use the :shadow subform within the defpackage form, such as,
    USER(82): (defpackage ch17
                (:shadow copy-list identity))
    #<The CH17 package>
    

  4. 17.9: My version of reverse1 in the book uses reverse2 as a help function to do all the work. As I say on page 111, "reverse1 does nothing on its own. It just initializes reverse2's second argument to be ()." An alternative technique is to combine the two functions into one with an optional argument:
    (defun reverse1 (list1 &optional (list2 '()))
      "Returns a list consisting of
       the members of LIST1 in reverse order
       followed by the members of LIST2 in original order.
       If LIST2 is omitted, returns a copy of LIST1
       with the order of members reversed."
      (check-type list1 list)
      (check-type list2 list)
      (if (null list1) list2
          (reverse1 (rest list1)
                    (cons (first list1) list2))))
    
    Here, if reverse1 is called with two arguments, list1 is bound to the first argument and list2 is bound to the second argument. However if reverse1 is called with only one argument, list1 is bound to that argument and list2 is bound to '().

    You should do Exercise 17.9 if you need concrete evidence that the reverse defined in the book is a lot slower than reverse1, in which case you may either use the reverse1/reverse2 pair, or the reverse1 given above with the optional argument.

  5. Do Exercises 17.9 and 17.10 if you feel they will help your understanding. Common Lisp already has the function substitute, which acts like subst*.

  6. You needn't do Exercises 17.13 - 17.28.

  7. Do Exercises 17.29 - 17.32, noting the following:

    • 17.29 & 17.30: Review the discussion of association lists in the Notes for Exercise 16.14 and for the function monthNumber. You are permitted to use assoc in the definitions you are writing, so your definitions of boundp and bound-to can be extremely short.

    • 17.31: If pat and lst are of different lengths, match should return NIL. The third argument of match1, called pairs in the text is a substitution (an association list). Review the discussion of reverse1 above. Rather than defining match and match1 as suggested in the book, you may prefer to define match to be a function that takes an optional argument that should default to ((T T)), for example
      (defun match (pat lst &optional (pairs '((T T))))
        ...
        )
      

    • 17.32: You could write your (substitute pat subs) either by recursing down pat or by recursing down subs. Since patterns tend to be a lot longer than substitutions, the former is a better idea, and that way, you get to use your boundp and bound-to functions.

    Submit your updated match.cl file. Please remember to revise the line "This file satisfies the exercises through Exercise ???"

Next

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

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