The less-familiar parts of Lisp for beginners — nconc

We next come to the nconc function.  The quick way to think of this is like append, but modifying its lists rather than creating a new list with the elements of the input lists.  The difference is quite important, and merits some discussion.

Recall that lists in Lisp are what a C++ programmer would call NULL-terminated singly-linked lists.  Each cell in the list consists of two parts, a value part (the car), which can hold any data type, including other lists, and the next-pointer part (the cdr).

When the append function is called on, say, two lists, then a third list is created, one whose value parts of the cells are the same as those of the input lists, but whose next-pointer parts are unique to this new list, as they point to new cells that were created to support the new list.

When the nconc function is called on two lists, the NULL-termination of the first list is changed to point at the head of the second list.  This means that the original list is modified.  The programmer must also take care to avoid the unintentional creation of circular list structures, as these require some careful coding to handle.  Constructs like dolist and mapcar will, left to themselves, loop forever when the list is circular.

Here is some sample code to demonstrate the difference:
 

CL-USER> (defparameter *list-a* (list 1 2 3))
*LIST-A*
CL-USER> (defparameter *list-b* (list 4 5 6))
*LIST-B*
CL-USER> (append *list-a* *list-b*)
(1 2 3 4 5 6)
CL-USER> *list-a*
(1 2 3)
CL-USER> *list-b*
(4 5 6)
CL-USER> (nconc *list-a* *list-b*)
(1 2 3 4 5 6)
CL-USER> *list-a*
(1 2 3 4 5 6)
CL-USER> *list-b*
(4 5 6)
CL-USER> (setf *print-circle* t)
T
CL-USER> (nconc *list-a* *list-b*)
(1 2 3 . #1=(4 5 6 . #1#))
CL-USER> (dolist (var *list-a*)
           (format t "~A " var)
           (sleep 0.2))
1 2 3 4 5 6 4 5 6 4 5 6 4 5 6 4 5 6 4 5 6 4 5 6 4 5 6 4 5 ; Evaluation aborted on NIL.

In the final example, I used the Slime Interrupt Command key sequence CTRL-C CTRL-C, as otherwise the code would have run forever.  The special variable *print-circle* is defined in the standard as allowing the printing of circular objects such as the list we created above.  It uses a different code path to detect cycles and print them in a standard-defined way.

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.