diff options
Diffstat (limited to 'third_party/lisp/alexandria/lists.lisp')
-rw-r--r-- | third_party/lisp/alexandria/lists.lisp | 367 |
1 files changed, 0 insertions, 367 deletions
diff --git a/third_party/lisp/alexandria/lists.lisp b/third_party/lisp/alexandria/lists.lisp deleted file mode 100644 index 51286071ebf2..000000000000 --- a/third_party/lisp/alexandria/lists.lisp +++ /dev/null @@ -1,367 +0,0 @@ -(in-package :alexandria) - -(declaim (inline safe-endp)) -(defun safe-endp (x) - (declare (optimize safety)) - (endp x)) - -(defun alist-plist (alist) - "Returns a property list containing the same keys and values as the -association list ALIST in the same order." - (let (plist) - (dolist (pair alist) - (push (car pair) plist) - (push (cdr pair) plist)) - (nreverse plist))) - -(defun plist-alist (plist) - "Returns an association list containing the same keys and values as the -property list PLIST in the same order." - (let (alist) - (do ((tail plist (cddr tail))) - ((safe-endp tail) (nreverse alist)) - (push (cons (car tail) (cadr tail)) alist)))) - -(declaim (inline racons)) -(defun racons (key value ralist) - (acons value key ralist)) - -(macrolet - ((define-alist-get (name get-entry get-value-from-entry add doc) - `(progn - (declaim (inline ,name)) - (defun ,name (alist key &key (test 'eql)) - ,doc - (let ((entry (,get-entry key alist :test test))) - (values (,get-value-from-entry entry) entry))) - (define-setf-expander ,name (place key &key (test ''eql) - &environment env) - (multiple-value-bind - (temporary-variables initforms newvals setter getter) - (get-setf-expansion place env) - (when (cdr newvals) - (error "~A cannot store multiple values in one place" ',name)) - (with-unique-names (new-value key-val test-val alist entry) - (values - (append temporary-variables - (list alist - key-val - test-val - entry)) - (append initforms - (list getter - key - test - `(,',get-entry ,key-val ,alist :test ,test-val))) - `(,new-value) - `(cond - (,entry - (setf (,',get-value-from-entry ,entry) ,new-value)) - (t - (let ,newvals - (setf ,(first newvals) (,',add ,key ,new-value ,alist)) - ,setter - ,new-value))) - `(,',get-value-from-entry ,entry)))))))) - (define-alist-get assoc-value assoc cdr acons -"ASSOC-VALUE is an alist accessor very much like ASSOC, but it can -be used with SETF.") - (define-alist-get rassoc-value rassoc car racons -"RASSOC-VALUE is an alist accessor very much like RASSOC, but it can -be used with SETF.")) - -(defun malformed-plist (plist) - (error "Malformed plist: ~S" plist)) - -(defmacro doplist ((key val plist &optional values) &body body) - "Iterates over elements of PLIST. BODY can be preceded by -declarations, and is like a TAGBODY. RETURN may be used to terminate -the iteration early. If RETURN is not used, returns VALUES." - (multiple-value-bind (forms declarations) (parse-body body) - (with-gensyms (tail loop results) - `(block nil - (flet ((,results () - (let (,key ,val) - (declare (ignorable ,key ,val)) - (return ,values)))) - (let* ((,tail ,plist) - (,key (if ,tail - (pop ,tail) - (,results))) - (,val (if ,tail - (pop ,tail) - (malformed-plist ',plist)))) - (declare (ignorable ,key ,val)) - ,@declarations - (tagbody - ,loop - ,@forms - (setf ,key (if ,tail - (pop ,tail) - (,results)) - ,val (if ,tail - (pop ,tail) - (malformed-plist ',plist))) - (go ,loop)))))))) - -(define-modify-macro appendf (&rest lists) append - "Modify-macro for APPEND. Appends LISTS to the place designated by the first -argument.") - -(define-modify-macro nconcf (&rest lists) nconc - "Modify-macro for NCONC. Concatenates LISTS to place designated by the first -argument.") - -(define-modify-macro unionf (list &rest args) union - "Modify-macro for UNION. Saves the union of LIST and the contents of the -place designated by the first argument to the designated place.") - -(define-modify-macro nunionf (list &rest args) nunion - "Modify-macro for NUNION. Saves the union of LIST and the contents of the -place designated by the first argument to the designated place. May modify -either argument.") - -(define-modify-macro reversef () reverse - "Modify-macro for REVERSE. Copies and reverses the list stored in the given -place and saves back the result into the place.") - -(define-modify-macro nreversef () nreverse - "Modify-macro for NREVERSE. Reverses the list stored in the given place by -destructively modifying it and saves back the result into the place.") - -(defun circular-list (&rest elements) - "Creates a circular list of ELEMENTS." - (let ((cycle (copy-list elements))) - (nconc cycle cycle))) - -(defun circular-list-p (object) - "Returns true if OBJECT is a circular list, NIL otherwise." - (and (listp object) - (do ((fast object (cddr fast)) - (slow (cons (car object) (cdr object)) (cdr slow))) - (nil) - (unless (and (consp fast) (listp (cdr fast))) - (return nil)) - (when (eq fast slow) - (return t))))) - -(defun circular-tree-p (object) - "Returns true if OBJECT is a circular tree, NIL otherwise." - (labels ((circularp (object seen) - (and (consp object) - (do ((fast (cons (car object) (cdr object)) (cddr fast)) - (slow object (cdr slow))) - (nil) - (when (or (eq fast slow) (member slow seen)) - (return-from circular-tree-p t)) - (when (or (not (consp fast)) (not (consp (cdr slow)))) - (return - (do ((tail object (cdr tail))) - ((not (consp tail)) - nil) - (let ((elt (car tail))) - (circularp elt (cons object seen)))))))))) - (circularp object nil))) - -(defun proper-list-p (object) - "Returns true if OBJECT is a proper list." - (cond ((not object) - t) - ((consp object) - (do ((fast object (cddr fast)) - (slow (cons (car object) (cdr object)) (cdr slow))) - (nil) - (unless (and (listp fast) (consp (cdr fast))) - (return (and (listp fast) (not (cdr fast))))) - (when (eq fast slow) - (return nil)))) - (t - nil))) - -(deftype proper-list () - "Type designator for proper lists. Implemented as a SATISFIES type, hence -not recommended for performance intensive use. Main usefullness as a type -designator of the expected type in a TYPE-ERROR." - `(and list (satisfies proper-list-p))) - -(defun circular-list-error (list) - (error 'type-error - :datum list - :expected-type '(and list (not circular-list)))) - -(macrolet ((def (name lambda-list doc step declare ret1 ret2) - (assert (member 'list lambda-list)) - `(defun ,name ,lambda-list - ,doc - (do ((last list fast) - (fast list (cddr fast)) - (slow (cons (car list) (cdr list)) (cdr slow)) - ,@(when step (list step))) - (nil) - (declare (dynamic-extent slow) ,@(when declare (list declare)) - (ignorable last)) - (when (safe-endp fast) - (return ,ret1)) - (when (safe-endp (cdr fast)) - (return ,ret2)) - (when (eq fast slow) - (circular-list-error list)))))) - (def proper-list-length (list) - "Returns length of LIST, signalling an error if it is not a proper list." - (n 1 (+ n 2)) - ;; KLUDGE: Most implementations don't actually support lists with bignum - ;; elements -- and this is WAY faster on most implementations then declaring - ;; N to be an UNSIGNED-BYTE. - (fixnum n) - (1- n) - n) - - (def lastcar (list) - "Returns the last element of LIST. Signals a type-error if LIST is not a -proper list." - nil - nil - (cadr last) - (car fast)) - - (def (setf lastcar) (object list) - "Sets the last element of LIST. Signals a type-error if LIST is not a proper -list." - nil - nil - (setf (cadr last) object) - (setf (car fast) object))) - -(defun make-circular-list (length &key initial-element) - "Creates a circular list of LENGTH with the given INITIAL-ELEMENT." - (let ((cycle (make-list length :initial-element initial-element))) - (nconc cycle cycle))) - -(deftype circular-list () - "Type designator for circular lists. Implemented as a SATISFIES type, so not -recommended for performance intensive use. Main usefullness as the -expected-type designator of a TYPE-ERROR." - `(satisfies circular-list-p)) - -(defun ensure-car (thing) - "If THING is a CONS, its CAR is returned. Otherwise THING is returned." - (if (consp thing) - (car thing) - thing)) - -(defun ensure-cons (cons) - "If CONS is a cons, it is returned. Otherwise returns a fresh cons with CONS - in the car, and NIL in the cdr." - (if (consp cons) - cons - (cons cons nil))) - -(defun ensure-list (list) - "If LIST is a list, it is returned. Otherwise returns the list designated by LIST." - (if (listp list) - list - (list list))) - -(defun remove-from-plist (plist &rest keys) - "Returns a propery-list with same keys and values as PLIST, except that keys -in the list designated by KEYS and values corresponding to them are removed. -The returned property-list may share structure with the PLIST, but PLIST is -not destructively modified. Keys are compared using EQ." - (declare (optimize (speed 3))) - ;; FIXME: possible optimization: (remove-from-plist '(:x 0 :a 1 :b 2) :a) - ;; could return the tail without consing up a new list. - (loop for (key . rest) on plist by #'cddr - do (assert rest () "Expected a proper plist, got ~S" plist) - unless (member key keys :test #'eq) - collect key and collect (first rest))) - -(defun delete-from-plist (plist &rest keys) - "Just like REMOVE-FROM-PLIST, but this version may destructively modify the -provided PLIST." - (declare (optimize speed)) - (loop with head = plist - with tail = nil ; a nil tail means an empty result so far - for (key . rest) on plist by #'cddr - do (assert rest () "Expected a proper plist, got ~S" plist) - (if (member key keys :test #'eq) - ;; skip over this pair - (let ((next (cdr rest))) - (if tail - (setf (cdr tail) next) - (setf head next))) - ;; keep this pair - (setf tail rest)) - finally (return head))) - -(define-modify-macro remove-from-plistf (&rest keys) remove-from-plist - "Modify macro for REMOVE-FROM-PLIST.") -(define-modify-macro delete-from-plistf (&rest keys) delete-from-plist - "Modify macro for DELETE-FROM-PLIST.") - -(declaim (inline sans)) -(defun sans (plist &rest keys) - "Alias of REMOVE-FROM-PLIST for backward compatibility." - (apply #'remove-from-plist plist keys)) - -(defun mappend (function &rest lists) - "Applies FUNCTION to respective element(s) of each LIST, appending all the -all the result list to a single list. FUNCTION must return a list." - (loop for results in (apply #'mapcar function lists) - append results)) - -(defun setp (object &key (test #'eql) (key #'identity)) - "Returns true if OBJECT is a list that denotes a set, NIL otherwise. A list -denotes a set if each element of the list is unique under KEY and TEST." - (and (listp object) - (let (seen) - (dolist (elt object t) - (let ((key (funcall key elt))) - (if (member key seen :test test) - (return nil) - (push key seen))))))) - -(defun set-equal (list1 list2 &key (test #'eql) (key nil keyp)) - "Returns true if every element of LIST1 matches some element of LIST2 and -every element of LIST2 matches some element of LIST1. Otherwise returns false." - (let ((keylist1 (if keyp (mapcar key list1) list1)) - (keylist2 (if keyp (mapcar key list2) list2))) - (and (dolist (elt keylist1 t) - (or (member elt keylist2 :test test) - (return nil))) - (dolist (elt keylist2 t) - (or (member elt keylist1 :test test) - (return nil)))))) - -(defun map-product (function list &rest more-lists) - "Returns a list containing the results of calling FUNCTION with one argument -from LIST, and one from each of MORE-LISTS for each combination of arguments. -In other words, returns the product of LIST and MORE-LISTS using FUNCTION. - -Example: - - (map-product 'list '(1 2) '(3 4) '(5 6)) - => ((1 3 5) (1 3 6) (1 4 5) (1 4 6) - (2 3 5) (2 3 6) (2 4 5) (2 4 6)) -" - (labels ((%map-product (f lists) - (let ((more (cdr lists)) - (one (car lists))) - (if (not more) - (mapcar f one) - (mappend (lambda (x) - (%map-product (curry f x) more)) - one))))) - (%map-product (ensure-function function) (cons list more-lists)))) - -(defun flatten (tree) - "Traverses the tree in order, collecting non-null leaves into a list." - (let (list) - (labels ((traverse (subtree) - (when subtree - (if (consp subtree) - (progn - (traverse (car subtree)) - (traverse (cdr subtree))) - (push subtree list))))) - (traverse tree)) - (nreverse list))) |