;; Copyright (C) 2002-2004, Yuji Minejima <ggb01164@nifty.ne.jp>
;; ALL RIGHTS RESERVED.
;;
;; $Id: cons.lisp,v 1.4 2004/02/20 07:23:42 yuji Exp $
;;
;; Redistribution and use in source and binary forms, with or without
;; modification, are permitted provided that the following conditions
;; are met:
;;
;; * Redistributions of source code must retain the above copyright
;; notice, this list of conditions and the following disclaimer.
;; * Redistributions in binary form must reproduce the above copyright
;; notice, this list of conditions and the following disclaimer in
;; the documentation and/or other materials provided with the
;; distribution.
;;
;; THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
;; "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
;; LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
;; A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
;; OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
;; SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
;; LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
;; DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
;; THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
;; (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
;; OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
(defunatom (object)
"Return true if OBJECT is of type atom; otherwise, return false."
(not (consp object)))
(defuncaar (list)
"Return the car of the car of LIST."
(car (car list)))
(defuncadr (list)
"Return the car of the cdr of LIST."
(car (cdr list)))
(defuncdar (list)
"Return the cdr of the car of LIST."
(cdr (car list)))
(defuncddr (list)
"Return the cdr of the cdr of LIST."
(cdr (cdr list)))
(defuncaaar (list)
"Return the car of the caar of LIST."
(car (caar list)))
(defuncaadr (list)
"Return the car of the cadr of LIST."
(car (cadr list)))
(defuncadar (list)
"Return the car of the cdar of LIST."
(car (cdar list)))
(defuncaddr (list)
"Return the car of the cddr of LIST."
(car (cddr list)))
(defuncdaar (list)
"Return the cdr of the caar of LIST."
(cdr (caar list)))
(defuncdadr (list)
"Return the cdr of the cadr of LIST."
(cdr (cadr list)))
(defuncddar (list)
"Return the cdr of the cdar of LIST."
(cdr (cdar list)))
(defuncdddr (list)
"Return the cdr of the cddr of LIST."
(cdr (cddr list)))
(defuncaaaar (list)
"Return the car of the caaar of LIST."
(car (caaar list)))
(defuncaaadr (list)
"Return the car of the caadr of LIST."
(car (caadr list)))
(defuncaadar (list)
"Return the car of the cadar of LIST."
(car (cadar list)))
(defuncaaddr (list)
"Return the car of the caddr of LIST."
(car (caddr list)))
(defuncadaar (list)
"Return the car of the cdaar of LIST."
(car (cdaar list)))
(defuncadadr (list)
"Return the car of the cdadr of LIST."
(car (cdadr list)))
(defuncaddar (list)
"Return the car of the cddar of LIST."
(car (cddar list)))
(defuncadddr (list)
"Return the car of the cdddr of LIST."
(car (cdddr list)))
(defuncdaaar (list)
"Return the cdr of the caaar of LIST."
(cdr (caaar list)))
(defuncdaadr (list)
"Return the cdr of the caadr of LIST."
(cdr (caadr list)))
(defuncdadar (list)
"Return the cdr of the cadar of LIST."
(cdr (cadar list)))
(defuncdaddr (list)
"Return the cdr of the caddr of LIST."
(cdr (caddr list)))
(defuncddaar (list)
"Return the cdr of the cdaar of LIST."
(cdr (cdaar list)))
(defuncddadr (list)
"Return the cdr of the cdadr of LIST."
(cdr (cdadr list)))
(defuncdddar (list)
"Return the cdr of the cddar of LIST."
(cdr (cddar list)))
(defuncddddr (list)
"Return the cdr of the cdddr of LIST."
(cdr (cdddr list)))
(defsetfcaar (x) (v)
`(progn
(rplaca (car ,x) ,v)
,v))
(defsetfcadr (x) (v)
`(progn
(rplaca (cdr ,x) ,v)
,v))
(defsetfcdar (x) (v)
`(progn
(rplacd (car ,x) ,v)
,v))
(defsetfcddr (x) (v)
`(progn
(rplacd (cdr ,x) ,v)
,v))
(defsetfcaaar (x) (v)
`(progn
(rplaca (caar ,x) ,v)
,v))
(defsetfcaadr (x) (v)
`(progn
(rplaca (cadr ,x) ,v)
,v))
(defsetfcadar (x) (v)
`(progn
(rplaca (cdar ,x) ,v)
,v))
(defsetfcaddr (x) (v)
`(progn
(rplaca (cddr ,x) ,v)
,v))
(defsetfcdaar (x) (v)
`(progn
(rplacd (caar ,x) ,v)
,v))
(defsetfcdadr (x) (v)
`(progn
(rplacd (cadr ,x) ,v)
,v))
(defsetfcddar (x) (v)
`(progn
(rplacd (cdar ,x) ,v)
,v))
(defsetfcdddr (x) (v)
`(progn
(rplacd (cddr ,x) ,v)
,v))
(defsetfcaaaar (x) (v)
`(progn
(rplaca (caaar ,x) ,v)
,v))
(defsetfcaaadr (x) (v)
`(progn
(rplaca (caadr ,x) ,v)
,v))
(defsetfcaadar (x) (v)
`(progn
(rplaca (cadar ,x) ,v)
,v))
(defsetfcaaddr (x) (v)
`(progn
(rplaca (caddr ,x) ,v)
,v))
(defsetfcadaar (x) (v)
`(progn
(rplaca (cdaar ,x) ,v)
,v))
(defsetfcadadr (x) (v)
`(progn
(rplaca (cdadr ,x) ,v)
,v))
(defsetfcaddar (x) (v)
`(progn
(rplaca (cddar ,x) ,v)
,v))
(defsetfcadddr (x) (v)
`(progn
(rplaca (cdddr ,x) ,v)
,v))
(defsetfcdaaar (x) (v)
`(progn
(rplacd (caaar ,x) ,v)
,v))
(defsetfcdaadr (x) (v)
`(progn
(rplacd (caadr ,x) ,v)
,v))
(defsetfcdadar (x) (v)
`(progn
(rplacd (cadar ,x) ,v)
,v))
(defsetfcdaddr (x) (v)
`(progn
(rplacd (caddr ,x) ,v)
,v))
(defsetfcddaar (x) (v)
`(progn
(rplacd (cdaar ,x) ,v)
,v))
(defsetfcddadr (x) (v)
`(progn
(rplacd (cdadr ,x) ,v)
,v))
(defsetfcdddar (x) (v)
`(progn
(rplacd (cddar ,x) ,v)
,v))
(defsetfcddddr (x) (v)
`(progn
(rplacd (cdddr ,x) ,v)
,v))
(defunnull (object)
"Return t if OBJECT is the empty list; otherwise, return nil."
(eq object nil))
(defunendp (list)
"Return true if LIST is the empty list. Returns false if LIST is a cons."
(check-type list list)
(null list))
(defunlistp (object)
"Return true if OBJECT is of type list; otherwise, return false."
(or (null object) (consp object)))
(defunfirst (list)
"Return the 1st element in LIST or NIL if LIST is empty."
(car list))
(defunsecond (list)
"Return the 2nd element in LIST or NIL if there is no 2nd element."
(cadr list))
(defunthird (list)
"Returns the 3rd element in LIST or NIL if there is no 3rd element."
(caddr list))
(defunfourth (list)
"Return the 4th element in LIST or NIL if there is no 4th element."
(cadddr list))
(defunfifth (list)
"Return the 5th element in LIST or NIL if there is no 5th element."
(car (cddddr list)))
(defunsixth (list)
"Return the 6th element in LIST or NIL if there is no 6th element."
(cadr (cddddr list)))
(defunseventh (list)
"Return the 7th element in LIST or NIL if there is no 7th element."
(caddr (cddddr list)))
(defuneighth (list)
"Return the 8th element in LIST or NIL if there is no 8th element."
(cadddr (cddddr list)))
(defunninth (list)
"Return the 9th element in LIST or NIL if there is no 9th element."
(car (cddddr (cddddr list))))
(defuntenth (list)
"Return the 10th element in LIST or NIL if there is no 10th element."
(cadr (cddddr (cddddr list))))
(defunrest (list)
"Perform the same operation as cdr."
(cdr list))
(defsetffirst (x) (v)
`(setf (car ,x) ,v))
(defsetfsecond (x) (v)
`(setf (cadr ,x) ,v))
(defsetfthird (x) (v)
`(setf (caddr ,x) ,v))
(defsetffourth (x) (v)
`(setf (cadddr ,x) ,v))
(defsetffifth (x) (v)
`(setf (car (cddddr ,x)) ,v))
(defsetfsixth (x) (v)
`(setf (cadr (cddddr ,x)) ,v))
(defsetfseventh (x) (v)
`(setf (caddr (cddddr ,x)) ,v))
(defsetfeighth (x) (v)
`(setf (cadddr (cddddr ,x)) ,v))
(defsetfninth (x) (v)
`(setf (car (cddddr (cddddr ,x))) ,v))
(defsetftenth (x) (v)
`(setf (cadr (cddddr (cddddr ,x))) ,v))
(defsetfrest (x) (v)
`(setf (cdr ,x) ,v))
(defunnthcdr (n list)
"Return the tail of LIST that would be obtained by calling cdr N times."
(check-type n (integer 0))
(do ((i n (1- i))
(result list (cdr result)))
((zerop i) result)))
(defunnth (n list)
"Return the Nth element in LIST, where the car is the zero-th element."
(car (nthcdr n list)))
(defsetfnth (n list) (v)
`(setf (car (nthcdr ,n ,list)) ,v))
(defuncopy-list (list)
"Return a copy of LIST which is either a proper list or a dotted list."
(unless (null list)
(let ((result (cons (car list) nil)))
(do ((x (cdr list) (cdr x))
(splice result (cdr (rplacd splice (cons (car x) nil)))))
((atom x) (rplacd splice x)))
result)))
(defunlist (&rest args)
"Return ARGS which is a list of supplied arguments."
(copy-list args))
(defunlist* (arg &rest others)
"Return a list of the arguments with the last cons being a dotted pair."
(cond ((null others) arg)
((null (cdr others)) (cons arg (car others)))
(t (let ((others (copy-list others)))
(do ((x others (cdr x)))
((null (cddr x)) (rplacd x (cadr x))))
(cons arg others)))))
(defunlist-length (list)
"Return the length of the given LIST, or nil if the LIST is circular."
(do ((n 0 (+ n 2)) ;Counter.
(fast list (cddr fast)) ;Fast pointer: leaps by 2.
(slow list (cdr slow))) ;Slow pointer: leaps by 1.
(nil)
;; If fast pointer hits the end, return the count.
(when (endp fast) (return n))
(when (endp (cdr fast)) (return (+ n 1)))
;; If fast pointer eventually equals slow pointer,
;; then we must be stuck in a circular list.
;; (A deeper property is the converse: if we are
;; stuck in a circular list, then eventually the
;; fast pointer will equal the slow pointer.
;; That fact justifies this implementation.)
(when (and (eq fast slow) (> n 0)) (return nil))))
(defunmake-list (size &key initial-element)
"Return a list of SIZE length, every element of which is INITIAL-ELEMENT."
(check-type size (integer 0))
(do ((i size (1- i))
(list '() (cons initial-element list)))
((zerop i) list)))
(defunlast (list &optional (n 1))
"Returns the last N conses (not the last N elements) of LIST."
(check-type n (integer 0))
(do ((l list (cdr l))
(r list)
(i 0 (1+ i)))
((atom l) r)
(if (>= i n) (pop r))))
(defunbutlast (list &optional (n 1))
"Return a copy of LIST from which the last N conses have been omitted."
(check-type n (integer 0))
(if (null list)
nil
(let ((length (do ((p (cdr list) (cdr p))
(i 1 (1+ i)))
((atom p) i))))
(do* ((here list (cdr here))
(result (list nil))
(splice result)
(count (- length n) (1- count)))
((<= count 0) (cdr result))
(setq splice (cdr (rplacd splice (list (car here)))))))))
(defunnbutlast (list &optional (n 1))
"Modify LIST to remove the last N conses."
(check-type n (integer 0))
(if (null list)
nil
(let ((length (do ((p (cdr list) (cdr p))
(i 1 (1+ i)))
((atom p) i))))
(unless (<= length n)
(do ((1st (cdr list) (cdr 1st))
(2nd list 1st)
(count (- length n 1) (1- count)))
((zerop count) (rplacd 2nd ()) list))))))
(defunnconc (&rest lists)
"Concatenate LISTS by changing them."
(setq lists (do ((p lists (cdr p)))
((or (car p) (null p)) p)))
(do* ((top (car lists))
(splice top)
(here (cdr lists) (cdr here)))
((null here) top)
(rplacd (last splice) (car here))
(when (car here)
(setq splice (car here)))))
(defunappend (&rest lists)
"Concatenate LISTS by copying them."
(setq lists (do ((p lists (cdr p)))
((or (car p) (null p)) p)))
(cond
((null lists) '())
((null (cdr lists)) (car lists))
(t (let* ((top (list (caar lists)))
(splice top))
(do ((x (cdar lists) (cdr x)))
((atom x))
(setq splice (cdr (rplacd splice (list (car x))))))
(do ((p (cdr lists) (cdr p)))
((null (cdr p)) (rplacd splice (car p)) top)
(do ((x (car p) (cdr x)))
((atom x))
(setq splice (cdr (rplacd splice (list (car x)))))))))))
(defunrevappend (list tail)
"Return (nconc (reverse list) tail)."
(do ((top list (cdr top))
(result tail (cons (car top) result)))
((endp top) result)))
(defunnreconc (list tail)
"Return (nconc (nreverse list) tail)."
(do ((1st (cdr list) (if (atom 1st) 1st (cdr 1st)))
(2nd list 1st)
(3rd tail 2nd))
((atom 2nd) 3rd)
(rplacd 2nd 3rd)))
(defuntailp (object list)
"Return true if OBJECT is the same as some tail of LIST, otherwise false."
(if (null list)
(null object)
(do ((list list (cdr list)))
((atom (cdr list)) (or (eql object list) (eql object (cdr list))))
(if (eql object list)
(return t)))))
(defunldiff (list object)
"Return a copy of LIST before the part which is the same as OBJECT."
(unless (eql list object)
(do* ((result (list (car list)))
(splice result)
(list (cdr list) (cdr list)))
((atom list) (when (eql list object) (rplacd splice nil)) result)
(if (eql list object)
(return result)
(setq splice (cdr (rplacd splice (list (car list)))))))))
(defunacons (key datum alist)
"Construct a new alist by adding the pair (key . datum) to alist."
(cons (cons key datum) alist))
(defunassoc (item alist &key key (test #'eql) test-not)
"Return the cons in ALIST whose car is equal (by TEST) to ITEM."
(when test-not
(setq test (complement test-not)))
(dolist (pair alist nil)
(when (and pair (funcall test item (apply-key key (car pair)))
(return pair)))))
(defunassoc-if (predicate alist &key key)
"Return the cons in ALIST whose car satisfies PREDICATE."
(dolist (pair alist nil)
(when (and pair (funcall predicate (apply-key key (car pair))))
(return pair))))
(defunassoc-if-not (predicate alist &key key)
"Return the cons in ALIST whose car does not satisfy PREDICATE."
(assoc-if (complement predicate) alist :key key))
(defunrassoc (item alist &key key (test #'eql) test-not)
"Return the cons in ALIST whose cdr is equal (by TEST) to ITEM."
(when test-not
(setq test (complement test-not)))
(dolist (pair alist nil)
(when (and pair (funcall test item (apply-key key (cdr pair))))
(return pair))))
(defunrassoc-if (predicate alist &key key)
"Return the cons in ALIST whose cdr satisfies PREDICATE."
(dolist (pair alist nil)
(when (and pair (funcall predicate (apply-key key (cdr pair))))
(return pair))))
(defunrassoc-if-not (predicate alist &key key)
"Return the cons in ALIST whose cdr does not satisfy PREDICATE."
(rassoc-if (complement predicate) alist :key key))
(defuncopy-alist (alist)
"Return a copy of ALIST."
(if (null alist)
nil
(let* ((new-alist (list (cons (caar alist) (cdar alist))))
(tail new-alist))
(dolist (pair (cdr alist))
(rplacd tail (list (cons (car pair) (cdr pair))))
(setq tail (cdr tail)))
new-alist)))
(defunpairlis (keys data &optional (alist '()))
"Construct an association list from keys and data (adding to alist)"
(do ((x keys (cdr x))
(y data (cdr y)))
((endp x) alist)
(setq alist (acons (car x) (car y) alist))))
(defunsublis (alist tree &key key (test #'eql) test-not)
"Substitute data of ALIST for subtrees matching keys of ALIST."
(when test-not
(setq test (complement test-not)))
(labels ((sub (subtree)
(let ((assoc (assoc (apply-key key subtree) alist :test test)))
(cond
(assoc (cdr assoc))
((atom subtree) subtree)
(t (let ((car (sub (car subtree)))
(cdr (sub (cdr subtree))))
(if (and (eq car (car subtree)) (eq cdr (cdr subtree)))
subtree
(cons car cdr))))))))
(sub tree)))
(defunnsublis (alist tree &key key (test #'eql) test-not)
"Substitute data of ALIST for subtrees matching keys of ALIST destructively."
(when test-not
(setq test (complement test-not)))
(labels ((sub (subtree)
(let ((assoc (assoc (apply-key key subtree) alist :test test)))
(cond
(assoc (cdr assoc))
((atom subtree) subtree)
(t
(rplaca subtree (sub (car subtree)))
(rplacd subtree (sub (cdr subtree)))
subtree)))))
(sub tree)))
(defuncopy-tree (tree)
"Create a copy of TREE (a structure of conses)."
(if (consp tree)
(cons (copy-tree (car tree)) (copy-tree (cdr tree)))
tree))
(defunsubst (new old tree &key key (test #'eql) test-not)
"Substitute NEW for subtrees matching OLD."
(when test-not
(setq test (complement test-not)))
(labels ((sub (subtree)
(cond
((funcall test old (apply-key key subtree)) new)
((atom subtree) subtree)
(t (let ((car (sub (car subtree)))
(cdr (sub (cdr subtree))))
(if (and (eq car (car subtree)) (eq cdr (cdr subtree)))
subtree
(cons car cdr)))))))
(sub tree)))
(defunnsubst (new old tree &key key (test #'eql) test-not)
"Substitute NEW for subtrees matching OLD destructively."
(when test-not
(setq test (complement test-not)))
(labels ((sub (subtree)
(cond
((funcall test old (apply-key key subtree)) new)
((atom subtree) subtree)
(t (rplaca subtree (sub (car subtree)))
(rplacd subtree (sub (cdr subtree)))
subtree))))
(sub tree)))
(defunsubst-if (new predicate tree &key key)
"Substitute NEW for subtrees for which PREDICATE is true."
(labels ((sub (subtree)
(cond
((funcall predicate (apply-key key subtree)) new)
((atom subtree) subtree)
(t (let ((car (sub (car subtree)))
(cdr (sub (cdr subtree))))
(if (and (eq car (car subtree)) (eq cdr (cdr subtree)))
subtree
(cons car cdr)))))))
(sub tree)))
(defunsubst-if-not (new predicate tree &key key)
"Substitute NEW for subtrees for which PREDICATE is false."
(subst-if new (complement predicate) tree :key key))
(defunnsubst-if (new predicate tree &key key)
"Substitute NEW for subtrees for which PREDICATE is true destructively."
(labels ((sub (subtree)
(cond
((funcall predicate (apply-key key subtree)) new)
((atom subtree) subtree)
(t (rplaca subtree (sub (car subtree)))
(rplacd subtree (sub (cdr subtree)))
subtree))))
(sub tree)))
(defunnsubst-if-not (new predicate tree &key key)
"Substitute NEW for subtrees for which PREDICATE is false destructively."
(nsubst-if new (complement predicate) tree :key key))
(defuntree-equal (a b &key (test #'eql) test-not)
"Test whether two trees are of the same shape and have the same leaves."
(when test-not
(setq test (complement test-not)))
(labels ((teq (a b)
(if (atom a)
(and (atom b) (funcall test a b))
(and (consp b)
(teq (car a) (car b))
(teq (cdr a) (cdr b))))))
(teq a b)))
(defmacropush (item place &environment env)
"Prepend item to the list in PLACE, store the list in PLACE, and returns it."
(if (symbolp place)
`(setq ,place (cons ,item ,place))
(multiple-value-bind (temporary-vars values stores setter getter)
(get-setf-expansion place env)
(let ((item-var (gensym)))
`(let* ((,item-var ,item)
,@(mapcar #'list temporary-vars values)
(,(car stores) (cons ,item-var ,getter)))
,setter)))))
(defmacropop (place &environment env)
"Return the car of the list in PLACE, storing the cdr of it back into PLACE."
(if (symbolp place)
`(prog1 (car ,place) (setq ,place (cdr ,place)))
(multiple-value-bind (temporary-vars values stores setter getter)
(get-setf-expansion place env)
(let ((list-var (gensym)))
`(let* (,@(mapcar #'list temporary-vars values)
(,list-var ,getter)
(,(car stores) (cdr ,list-var)))
,setter
(car ,list-var))))))
(defunmember (item list &key key (test #'eql) test-not)
"Return the tail of LIST beginning with an element equal to ITEM."
(when test-not
(setq test (complement test-not)))
(do ((here list (cdr here)))
((or (null here) (funcall test item (apply-key key (car here)))) here)))
(defunmember-if (predicate list &key key)
"Return the tail of LIST beginning with an element satisfying PREDICATE."
(do ((here list (cdr here)))
((or (endp here) (funcall predicate (apply-key key (car here)))) here)))
(defunmember-if-not (predicate list &key key)
"Return the tail of LIST beginning with an element not satisfying PREDICATE."
(member-if (complement predicate) list :key key))
(defunadjoin (item list &key key (test #'eql) test-not)
"Add ITEM to LIST unless it is already a member."
(when test-not
(setq test (complement test-not)))
(if (member (apply-key key item) list :key key :test test)
list
(cons item list)))
(defunintersection (list-1 list-2 &key key (test #'eql) test-not)
"Return the intersection of LIST-1 and LIST-2."
(when test-not
(setq test (complement test-not)))
(let (result)
(dolist (element list-1)
(when (member (apply-key key element) list-2 :key key :test test)
(push element result)))
result))
(defunnintersection (list-1 list-2 &key key (test #'eql) test-not)
"Return the intersection of LIST-1 and LIST-2 destructively modifying LIST-1."
(when test-not
(setq test (complement test-not)))
(let* ((result (list nil))
(splice result))
(do ((list list-1 (cdr list)))
((endp list) (rplacd splice nil) (cdr result))
(when (member (apply-key key (car list)) list-2 :key key :test test)
(setq splice (cdr (rplacd splice list)))))))
(defununion (list-1 list-2 &key key (test #'eql) test-not)
"Return the union of LIST-1 and LIST-2."
(when test-not
(setq test (complement test-not)))
(let ((result list-2))
(dolist (element list-1)
(unless (member (apply-key key element) list-2 :key key :test test)
(push element result)))
result))
(defunnunion (list-1 list-2 &key key (test #'eql) test-not)
"Return the union of LIST-1 and LIST-2 destructively modifying them."
(when test-not
(setq test (complement test-not)))
(do* ((result list-2)
(list-1 list-1)
tmp)
((endp list-1) result)
(if (member (apply-key key (car list-1)) list-2 :key key :test test)
(setq list-1 (cdr list-1))
(setq tmp (cdr list-1)
result (rplacd list-1 result)
list-1 tmp))))
(defunsubsetp (list-1 list-2 &key key (test #'eql) test-not)
"Return T if every element in LIST-1 is also in LIST-2."
(when test-not
(setq test (complement test-not)))
(dolist (element list-1 t)
(unless (member (apply-key key element) list-2 :key key :test test)
(return nil))))
(defunset-difference (list-1 list-2 &key key (test #'eql) test-not)
"Return the elements of LIST-1 which are not in LIST-2."
(when test-not
(setq test (complement test-not)))
(let ((result nil))
(dolist (element list-1)
(unless (member (apply-key key element) list-2 :key key :test test)
(push element result)))
result))
(defunnset-difference (list-1 list-2 &key key (test #'eql) test-not)
"Return the elements of LIST-1 which are not in LIST-2, modifying LIST-1."
(when test-not
(setq test (complement test-not)))
(do* ((result nil)
(list-1 list-1)
tmp)
((endp list-1) result)
(if (member (apply-key key (car list-1)) list-2 :key key :test test)
(setq list-1 (cdr list-1))
(setq tmp (cdr list-1)
result (rplacd list-1 result)
list-1 tmp))))
(defunset-exclusive-or (list-1 list-2 &key key (test #'eql) test-not)
"Return a list of elements that appear in exactly one of LIST-1 and LIST-2."
(when test-not
(setq test (complement test-not)))
(let ((result nil))
(dolist (element list-1)
(unless (member (apply-key key element) list-2 :key key :test test)
(push element result)))
(dolist (element list-2)
(unless (member (apply-key key element) list-1 :key key :test test)
(push element result)))
result))
(defunnset-exclusive-or (list-1 list-2 &key key (test #'eql) test-not)
"The destructive version of set-exclusive-or."
(when test-not
(setq test (complement test-not)))
(do* ((head-1 (cons nil list-1))
(head-2 (cons nil list-2))
(p-1 head-1))
((or (endp (cdr p-1)) (endp (cdr head-2)))
(progn (rplacd (last p-1) (cdr head-2))
(cdr head-1)))
(do ((p-2 head-2 (cdr p-2)))
((endp (cdr p-2)) (setq p-1 (cdr p-1)))
(when (funcall test (apply-key key (cadr p-1)) (apply-key key (cadr p-2)))
(rplacd p-1 (cddr p-1))
(rplacd p-2 (cddr p-2))
(return)))))
(defmacropushnew (item place &rest keys &environment env)
"Test if ITEM is the same as any element of the list in PLACE. If not, prepend it to the list, then the new list is stored in PLACE."
(if (symbolp place)
`(setq ,place (adjoin ,item ,place ,@keys))
(multiple-value-bind (temporary-vars values stores setter getter)
(get-setf-expansion place env)
(let ((item-var (gensym)))
`(let* ((,item-var ,item)
,@(mapcar #'list temporary-vars values)
(,(car stores) (adjoin ,item-var ,getter ,@keys)))
,setter)))))
(defunmapc (function list &rest more-lists)
"Apply FUNCTION to successive elements of lists, return LIST."
(do* ((lists (cons list more-lists))
(args (make-list (length lists))))
((do ((l lists (cdr l))
(a args (cdr a)))
((or (null l) (endp (car l))) l)
(rplaca a (caar l))
(rplaca l (cdar l)))
list)
(apply function args)))
(defunmapcar (function list &rest more-lists)
"Apply FUNCTION to successive elements of lists, return list of results."
(do* ((lists (cons list more-lists))
(len (length lists))
(args (make-list len) (make-list len))
(result (list nil))
(splice result))
((do ((l lists (cdr l))
(a args (cdr a)))
((or (null l) (endp (car l))) l)
(rplaca a (caar l))
(rplaca l (cdar l)))
(cdr result))
(setq splice (cdr (rplacd splice (list (apply function args)))))))
(defunmapcan (function list &rest more-lists)
"Apply FUNCTION to successive elements of lists, return nconc of results."
(apply #'nconc (apply #'mapcar function list more-lists)))
(defunmapl (function list &rest more-lists)
"Apply FUNCTION to successive sublists of list, return LIST."
(do* ((lists (cons list more-lists)))
((member nil lists) list)
(apply function lists)
(do ((l lists (cdr l)))
((endp l))
(rplaca l (cdar l)))))
(defunmaplist (function list &rest more-lists)
"Apply FUNCTION to successive sublists of list, return list of results."
(do* ((lists (cons list more-lists))
(result (list nil))
(splice result))
((member nil lists) (cdr result))
(setq splice (cdr (rplacd splice (list (apply function lists)))))
(do ((l lists (cdr l)))
((endp l))
(rplaca l (cdar l)))))
(defunmapcon (function list &rest more-lists)
"Apply FUNCTION to successive sublists of lists, return nconc of results."
(apply #'nconc (apply #'maplist function list more-lists)))
(defunget-properties (plist indicator-list)
"Look up any of several property list entries all at once."
(do ((plist plist (cddr plist)))
((endp plist) (values nil nil nil))
(when (member (car plist) indicator-list)
(return (values (car plist) (cadr plist) plist)))))
(defungetf (plist indicator &optional (default ()))
"Find a property on PLIST whose property indicator is identical to INDICATOR, and returns its corresponding property value."
(do ((plist plist (cddr plist)))
((endp plist) default)
(when (eq indicator (car plist))
(return (cadr plist)))))
(define-setf-expandergetf (place indicator
&optional (default nil default-supplied)
&environment env)
(multiple-value-bind (temporary-vars values stores setter getter)
(get-setf-expansion place env)
(let ((value-var (gensym))
(indicator-var (gensym))
(place-var (gensym))
(tail-var (gensym))
(default-var (when default-supplied (gensym))))
(values
`(,@temporary-vars ,indicator-var ,@(when default-supplied
`(,default-var)))
`(,@values ,indicator ,@(when default-supplied `(,default)))
`(,value-var)
`(let ((,place-var ,getter))
(multiple-value-bind (,(gensym) ,(gensym) ,tail-var)
(get-properties ,place-var (list ,indicator-var))
(if ,tail-var
(rplaca (cdr ,tail-var) ,value-var)
(let ((,(car stores) (cons ,indicator-var
(cons ,value-var ,place-var))))
,setter
,value-var))
,value-var))
`(getf ,getter ,indicator-var ,@(when default-supplied
`(,default-var)))))))
(defmacroremf (place indicator &environment env)
"Remove from the property list stored in PLACE a property with a property indicator identical to INDICATOR."
(multiple-value-bind (temporary-vars values stores setter getter)
(get-setf-expansion place env)
(let ((indicator-var (gensym))
(plist-var (gensym))
(splice-var (gensym)))
`(do* (,@(mapcar #'list temporary-vars values)
(,indicator-var ,indicator)
(,plist-var (cons nil ,getter))
(,splice-var ,plist-var (cddr ,splice-var))
,(car stores))
((endp ,splice-var) nil)
(when (eq ,indicator-var (cadr ,splice-var))
(rplacd ,splice-var (cdddr ,splice-var))
(setq ,(car stores) (cdr ,plist-var))
,setter
(return t))))))