List-printing code for upcoming posts

I’m about to start what may be a long series of posts about the Lisp language for newcomers arriving from a C++ background.  Before I get to that, I’m going to write a bit of handy code to produce drawings of lists similar to those we often see in Lisp textbooks.

To review, lists in Lisp are a bit different from std::slist classes in the C++ STL.  In C++, a list has a single entry point, and, while you can have multiple references to the list, they all have the same view on the list.  In Lisp, you could think of a reference to a list as a data pair.  The reference has a pointer to the list object itself, but it also has a position marker, like an iterator, which is its entry point into the list.  Because lists in Lisp are singly-linked, you can have multiple references to the same underlying object, but they’ll have different views of the list.  To demonstrate this, here’s some printing code.

;; Some code to pretty-print a list with multiple entry points
(defparameter *rec-width* 10)

(defun plot-list-worker (&rest entries)
  "Given inputs of the form (list1 \"list1\") (list2 \"list2\")..., pretty-prints the list with multiple entry points.  That is, the first entry in 'lists' is the symbol-name proper superlist of the second, which is a proper superlist of the third, etc."
  (let* ((big-list (first (first entries)))
         (total-list-len (length big-list)))

    ;; print the entry points
    (let ((prev-indents '()))
      (dolist (entry entries)
        (let* ((e-handle (first entry))
               (e-name (second entry))
               (indent (1+ (* *rec-width* 
                              (- total-list-len 
                                 (length e-handle))))))
          (dolist (p-i (reverse prev-indents))
            (format t "~VT." (+ p-i 2)))
          (format t "~VT~A~%" indent e-name)
          (push indent prev-indents)))
      (dotimes (i 2)
        (dolist (p-i (reverse prev-indents))
          (format t "~VT." (+ p-i 2)))
        (format t "~%")))

    ;; print the boxes
    (dotimes (i total-list-len)
      (format t "~VT-----" (1+ (* i *rec-width*))))
    (format t "~%")
    (dotimes (i total-list-len)
      (format t "~VT|A|D| -->" (1+ (* i *rec-width*))))
    (format t "NIL~%")
    (dotimes (i total-list-len)
      (format t "~VT-----" (1+ (* i *rec-width*))))
    (format t "~%")

    ;; print the drops to the values
    (dotimes (i 2)
      (dotimes (j total-list-len)
        (format t "~VT |" (1+ (* j *rec-width*))))
      (format t "~%"))

    (dotimes (i total-list-len)
      (let ((j (- total-list-len i 1)))
        (dotimes (k j)
          (format t "~VT |" (1+ (* k *rec-width*))))
        (format t "~VT~A~%" (1+ (* j *rec-width*)) (nth j big-list))))

(defmacro plot-lists (&rest lists)
  "Set up and invoke plot-lists-worker"
  (let ((arglist))
    (dolist (lname lists)
      (push (list 'list lname (symbol-name lname)) arglist))
    `(plot-list-worker ,@(reverse arglist))))

Now, I can create a single list with multiple entry points, and plot them.  Here’s the invocation and output, from the SLIME development environment.

CL-USER> (let* ((clist (list 5)) 
                (blist (append (list 3 4) clist))
                (alist (append (list 1 2) blist)))
           (plot-lists alist blist clist))
   .                 BLIST
   .                   .                 CLIST
   .                   .                   .
   .                   .                   .
 -----     -----     -----     -----     -----
 |A|D| --> |A|D| --> |A|D| --> |A|D| --> |A|D| -->NIL
 -----     -----     -----     -----     -----
  |         |         |         |         |
  |         |         |         |         |
  |         |         |         |        5
  |         |         |        4
  |         |        3
  |        2

The ‘alist’ view on the list sees a list of 5 objects, while ‘blist’ cannot see the first two objects, and sees only a list of 3.

There is, however, another way in which Lisp lists differ from C++ std::slist objects.  Lists in Lisp can be spliced together.  It is possible to have two different lists which join partway down their lengths, so that they share all elements beyond the join.  I’m not going to try to draw that.  There is no parallel to this in C++ std::slist objects.

OK, with that taken care of, further posts will follow.

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>


反垃圾邮件 / Anti-spam question * Time limit is exhausted. Please reload CAPTCHA.