Adding elements to the end of a list is usually inefficient in Lisp:
- (append list (list item)) is the worst possible approach, because list gets copied every time a new item is added. If you use this form to build a list N long, you'll have done N squared cons's. Imagine doing that for a simple 100-element list!
- (nconc list (list item)) doesn't cons, but still gets very slow as the list gets long, because Lisp has to cdr all the way to the end of the list in order to find the last cons cell to modify.
A classic solution is to create a data structure called a tconc structure (for "tail concatenate"), which holds two pointers to the same list:
- a head pointer to the whole list, and
- a tail pointer to the last cons cell of that list.
With this data structure, you can add new elements to the end of the list with just a few quick operations, no matter how long the list is, and you can still get the whole list whenever you need it.
Therefore, your job is to:
- Define (make-tconc [ list ]) to return a tconc structure pointing to list. If no list is given, a tconc structure for an empty list should be returned.
- Define (tconc tconc-structure [item item ...]) to add the items, if any, to the end of the list pointed to by tconc-structure, update tconc-strcture appropriately, and return the new value of the internal list.
- Define (tconc-list tconc-structure list ) to add the items in list to the end of the internal list.
Note that you can get the internal list at any time with (tconc tconc-structure).
> (setq tc (make-tconc))
> (tconc tc 1)
(1)
> (tconc tc 2 3)
(1 2 3)
> (tconc tc)
(1 2 3)
Each successive call to tconc should be efficient, no matter how long the internal list has grown. One test of your tconc structure is that it always obeys the following rule:
(eq (last head-pointer) tail-pointer)
Solution:
(defstruct (tconc (:constructor create-tconc))
"a tail concatenate list for easy append"
(head nil) (tail nil))
(defun make-tconc (&optional l)
(let ((tc (create-tconc)))
(apply #'tconc tc l)
tc))
(defun tconc (tconc-structure &rest items)
(unless (null items)
(if (null (tconc-head tconc-structure))
(setf (tconc-head tconc-structure) items)
(setf (cdr (tconc-tail tconc-structure)) items))
(setf (tconc-tail tconc-structure) (last items)))
(tconc-head tconc-structure))
(defun tconc-list (tconc-structure &optional l)
(apply #'tconc tconc-structure l))
No comments:
Post a Comment