\f
;;;; utilities
-(eval-when (:compile-toplevel)
+(defun %check-generic-sequence-bounds (seq start end)
+ (let ((length (sb!sequence:length seq)))
+ (if (<= 0 start (or end length) length)
+ (or end length)
+ (sequence-bounding-indices-bad-error seq start end))))
+
+(eval-when (:compile-toplevel :load-toplevel :execute)
+
+(defparameter *sequence-keyword-info*
+ ;; (name default supplied-p adjustment new-type)
+ `((count nil
+ nil
+ (etypecase count
+ (null (1- most-positive-fixnum))
+ (fixnum (max 0 count))
+ (integer (if (minusp count)
+ 0
+ (1- most-positive-fixnum))))
+ (mod #.sb!xc:most-positive-fixnum))
+ ,@(mapcan (lambda (names)
+ (destructuring-bind (start end length sequence) names
+ (list
+ `(,start
+ 0
+ nil
+ (if (<= 0 ,start ,length)
+ ,start
+ (sequence-bounding-indices-bad-error ,sequence ,start ,end))
+ index)
+ `(,end
+ nil
+ nil
+ (if (or (null ,end) (<= ,start ,end ,length))
+ ;; Defaulting of NIL is done inside the
+ ;; bodies, for ease of sharing with compiler
+ ;; transforms.
+ ;;
+ ;; FIXME: defend against non-number non-NIL
+ ;; stuff?
+ ,end
+ (sequence-bounding-indices-bad-error ,sequence ,start ,end))
+ (or null index)))))
+ '((start end length sequence)
+ (start1 end1 length1 sequence1)
+ (start2 end2 length2 sequence2)))
+ (key nil
+ nil
+ (and key (%coerce-callable-to-fun key))
+ (or null function))
+ (test #'eql
+ nil
+ (%coerce-callable-to-fun test)
+ function)
+ (test-not nil
+ nil
+ (and test-not (%coerce-callable-to-fun test-not))
+ (or null function))
+ ))
+
+(sb!xc:defmacro define-sequence-traverser (name args &body body)
+ (multiple-value-bind (body declarations docstring)
+ (parse-body body :doc-string-allowed t)
+ (collect ((new-args) (new-declarations) (adjustments))
+ (dolist (arg args)
+ (case arg
+ ;; FIXME: make this robust. And clean.
+ ((sequence)
+ (new-args arg)
+ (adjustments '(length (length sequence)))
+ (new-declarations '(type index length)))
+ ((sequence1)
+ (new-args arg)
+ (adjustments '(length1 (length sequence1)))
+ (new-declarations '(type index length1)))
+ ((sequence2)
+ (new-args arg)
+ (adjustments '(length2 (length sequence2)))
+ (new-declarations '(type index length2)))
+ ((function predicate)
+ (new-args arg)
+ (adjustments `(,arg (%coerce-callable-to-fun ,arg))))
+ (t (let ((info (cdr (assoc arg *sequence-keyword-info*))))
+ (cond (info
+ (destructuring-bind (default supplied-p adjuster type) info
+ (new-args `(,arg ,default ,@(when supplied-p (list supplied-p))))
+ (adjustments `(,arg ,adjuster))
+ (new-declarations `(type ,type ,arg))))
+ (t (new-args arg)))))))
+ `(defun ,name ,(new-args)
+ ,@(when docstring (list docstring))
+ ,@declarations
+ (let* (,@(adjustments))
+ (declare ,@(new-declarations))
+ ,@body)))))
;;; SEQ-DISPATCH does an efficient type-dispatch on the given SEQUENCE.
;;;
;;; FIXME: It might be worth making three cases here, LIST,
;;; SIMPLE-VECTOR, and VECTOR, instead of the current LIST and VECTOR.
-;;; It tend to make code run faster but be bigger; some benchmarking
+;;; It tends to make code run faster but be bigger; some benchmarking
;;; is needed to decide.
-(sb!xc:defmacro seq-dispatch (sequence list-form array-form)
+(sb!xc:defmacro seq-dispatch
+ (sequence list-form array-form &optional other-form)
`(if (listp ,sequence)
- ,list-form
- ,array-form))
-
-(sb!xc:defmacro make-sequence-like (sequence length)
+ (let ((,sequence (truly-the list ,sequence)))
+ (declare (ignorable ,sequence))
+ ,list-form)
+ ,@(if other-form
+ `((if (arrayp ,sequence)
+ (let ((,sequence (truly-the vector ,sequence)))
+ (declare (ignorable ,sequence))
+ ,array-form)
+ ,other-form))
+ `((let ((,sequence (truly-the vector ,sequence)))
+ (declare (ignorable ,sequence))
+ ,array-form)))))
+
+(sb!xc:defmacro %make-sequence-like (sequence length)
#!+sb-doc
- "Returns a sequence of the same type as SEQUENCE and the given LENGTH."
- `(make-sequence-of-type (type-of ,sequence) ,length))
-
-(sb!xc:defmacro type-specifier-atom (type)
- #!+sb-doc "Returns the broad class of which TYPE is a specific subclass."
- `(if (atom ,type) ,type (car ,type)))
-
+ "Return a sequence of the same type as SEQUENCE and the given LENGTH."
+ `(seq-dispatch ,sequence
+ (make-list ,length)
+ (make-array ,length :element-type (array-element-type ,sequence))
+ (sb!sequence:make-sequence-like ,sequence ,length)))
+
+(sb!xc:defmacro bad-sequence-type-error (type-spec)
+ `(error 'simple-type-error
+ :datum ,type-spec
+ :expected-type '(satisfies is-a-valid-sequence-type-specifier-p)
+ :format-control "~S is a bad type specifier for sequences."
+ :format-arguments (list ,type-spec)))
+
+(sb!xc:defmacro sequence-type-length-mismatch-error (type length)
+ `(error 'simple-type-error
+ :datum ,length
+ :expected-type (cond ((array-type-p ,type)
+ `(eql ,(car (array-type-dimensions ,type))))
+ ((type= ,type (specifier-type 'null))
+ '(eql 0))
+ ((cons-type-p ,type)
+ '(integer 1))
+ (t (bug "weird type in S-T-L-M-ERROR")))
+ ;; FIXME: this format control causes ugly printing. There's
+ ;; probably some ~<~@:_~> incantation that would make it
+ ;; nicer. -- CSR, 2002-10-18
+ :format-control "The length requested (~S) does not match the type restriction in ~S."
+ :format-arguments (list ,length (type-specifier ,type))))
+
+(sb!xc:defmacro sequence-type-too-hairy (type-spec)
+ ;; FIXME: Should this be a BUG? I'm inclined to think not; there are
+ ;; words that give some but not total support to this position in
+ ;; ANSI. Essentially, we are justified in throwing this on
+ ;; e.g. '(OR SIMPLE-VECTOR (VECTOR FIXNUM)), but maybe not (by ANSI)
+ ;; on '(CONS * (CONS * NULL)) -- CSR, 2002-10-18
+
+ ;; On the other hand, I'm not sure it deserves to be a type-error,
+ ;; either. -- bem, 2005-08-10
+ `(error 'simple-program-error
+ :format-control "~S is too hairy for sequence functions."
+ :format-arguments (list ,type-spec)))
) ; EVAL-WHEN
+(defun is-a-valid-sequence-type-specifier-p (type)
+ (let ((type (specifier-type type)))
+ (or (csubtypep type (specifier-type 'list))
+ (csubtypep type (specifier-type 'vector)))))
+
;;; It's possible with some sequence operations to declare the length
;;; of a result vector, and to be safe, we really ought to verify that
;;; the actual result has the declared length.
(let ((actual-length (length vector)))
(unless (= actual-length declared-length)
(error 'simple-type-error
- :datum vector
- :expected-type `(vector ,declared-length)
- :format-control
- "Vector length (~D) doesn't match declared length (~D)."
- :format-arguments (list actual-length declared-length))))
+ :datum vector
+ :expected-type `(vector ,declared-length)
+ :format-control
+ "Vector length (~W) doesn't match declared length (~W)."
+ :format-arguments (list actual-length declared-length))))
vector)
(defun sequence-of-checked-length-given-type (sequence result-type)
(let ((ctype (specifier-type result-type)))
(if (not (array-type-p ctype))
- sequence
- (let ((declared-length (first (array-type-dimensions ctype))))
- (if (eq declared-length '*)
- sequence
- (vector-of-checked-length-given-length sequence
- declared-length))))))
-
-;;; Given an arbitrary type specifier, return a sane sequence type
-;;; specifier that we can directly match.
-(defun result-type-or-lose (type &optional nil-ok)
- (let ((type (specifier-type type)))
- (cond
- ((eq type *empty-type*)
- (if nil-ok
- nil
- (error 'simple-type-error
- :datum type
- :expected-type '(or vector cons)
- :format-control
- "NIL output type invalid for this sequence function."
- :format-arguments ())))
- ((dolist (seq-type '(list string simple-vector bit-vector))
- (when (csubtypep type (specifier-type seq-type))
- (return seq-type))))
- ((csubtypep type (specifier-type 'vector))
- (type-specifier type))
- (t
- (error 'simple-type-error
- :datum type
- :expected-type 'sequence
- :format-control
- "~S is not a legal type specifier for sequence functions."
- :format-arguments (list type))))))
-
+ sequence
+ (let ((declared-length (first (array-type-dimensions ctype))))
+ (if (eq declared-length '*)
+ sequence
+ (vector-of-checked-length-given-length sequence
+ declared-length))))))
+
+(declaim (ftype (function (sequence index) nil) signal-index-too-large-error))
(defun signal-index-too-large-error (sequence index)
(let* ((length (length sequence))
- (max-index (and (plusp length) (1- length))))
+ (max-index (and (plusp length)
+ (1- length))))
(error 'index-too-large-error
- :datum index
- :expected-type (if max-index
- `(integer 0 ,max-index)
- ;; This seems silly, is there something better?
- '(integer (0) (0))))))
-
-(defun make-sequence-of-type (type length)
- #!+sb-doc "Returns a sequence of the given TYPE and LENGTH."
- (declare (fixnum length))
- (case (type-specifier-atom type)
- (list (make-list length))
- ((bit-vector simple-bit-vector) (make-array length :element-type '(mod 2)))
- ((string simple-string base-string simple-base-string)
- (make-string length))
- (simple-vector (make-array length))
- ((array simple-array vector)
- (if (listp type)
- (make-array length :element-type (cadr type))
- (make-array length)))
- (t
- (make-sequence-of-type (result-type-or-lose type) length))))
+ :datum index
+ :expected-type (if max-index
+ `(integer 0 ,max-index)
+ ;; This seems silly, is there something better?
+ '(integer 0 (0))))))
+
+(declaim (ftype (function (t t t) nil) sequence-bounding-indices-bad-error))
+(defun sequence-bounding-indices-bad-error (sequence start end)
+ (let ((size (length sequence)))
+ (error 'bounding-indices-bad-error
+ :datum (cons start end)
+ :expected-type `(cons (integer 0 ,size)
+ (integer ,start ,size))
+ :object sequence)))
+
+(declaim (ftype (function (t t t) nil) array-bounding-indices-bad-error))
+(defun array-bounding-indices-bad-error (array start end)
+ (let ((size (array-total-size array)))
+ (error 'bounding-indices-bad-error
+ :datum (cons start end)
+ :expected-type `(cons (integer 0 ,size)
+ (integer ,start ,size))
+ :object array)))
+
+(declaim (ftype (function (t) nil) circular-list-error))
+(defun circular-list-error (list)
+ (let ((*print-circle* t))
+ (error 'simple-type-error
+ :format-control "List is circular:~% ~S"
+ :format-arguments (list list)
+ :datum list
+ :type '(and list (satisfies list-length)))))
+
\f
(defun elt (sequence index)
- #!+sb-doc "Returns the element of SEQUENCE specified by INDEX."
- (etypecase sequence
- (list
- (do ((count index (1- count))
- (list sequence (cdr list)))
- ((= count 0)
- (if (endp list)
- (signal-index-too-large-error sequence index)
- (car list)))
- (declare (type (integer 0) count))))
- (vector
- (when (>= index (length sequence))
- (signal-index-too-large-error sequence index))
- (aref sequence index))))
+ #!+sb-doc "Return the element of SEQUENCE specified by INDEX."
+ (seq-dispatch sequence
+ (do ((count index (1- count))
+ (list sequence (cdr list)))
+ ((= count 0)
+ (if (endp list)
+ (signal-index-too-large-error sequence index)
+ (car list)))
+ (declare (type (integer 0) count)))
+ (progn
+ (when (>= index (length sequence))
+ (signal-index-too-large-error sequence index))
+ (aref sequence index))
+ (sb!sequence:elt sequence index)))
(defun %setelt (sequence index newval)
#!+sb-doc "Store NEWVAL as the component of SEQUENCE specified by INDEX."
- (etypecase sequence
- (list
- (do ((count index (1- count))
- (seq sequence))
- ((= count 0) (rplaca seq newval) newval)
- (declare (fixnum count))
- (if (atom (cdr seq))
- (signal-index-too-large-error sequence index)
- (setq seq (cdr seq)))))
- (vector
- (when (>= index (length sequence))
- (signal-index-too-large-error sequence index))
- (setf (aref sequence index) newval))))
+ (seq-dispatch sequence
+ (do ((count index (1- count))
+ (seq sequence))
+ ((= count 0) (rplaca seq newval) newval)
+ (declare (fixnum count))
+ (if (atom (cdr seq))
+ (signal-index-too-large-error sequence index)
+ (setq seq (cdr seq))))
+ (progn
+ (when (>= index (length sequence))
+ (signal-index-too-large-error sequence index))
+ (setf (aref sequence index) newval))
+ (setf (sb!sequence:elt sequence index) newval)))
(defun length (sequence)
- #!+sb-doc "Returns an integer that is the length of SEQUENCE."
- (etypecase sequence
- (vector (length (truly-the vector sequence)))
- (list (length (truly-the list sequence)))))
+ #!+sb-doc "Return an integer that is the length of SEQUENCE."
+ (seq-dispatch sequence
+ (length sequence)
+ (length sequence)
+ (sb!sequence:length sequence)))
-(defun make-sequence (type length &key (initial-element NIL iep))
+(defun make-sequence (type length &key (initial-element nil iep))
#!+sb-doc
- "Returns a sequence of the given Type and Length, with elements initialized
- to :Initial-Element."
+ "Return a sequence of the given TYPE and LENGTH, with elements initialized
+ to INITIAL-ELEMENT."
(declare (fixnum length))
- (let ((type (specifier-type type)))
+ (let* ((adjusted-type
+ (typecase type
+ (atom (cond
+ ((eq type 'string) '(vector character))
+ ((eq type 'simple-string) '(simple-array character (*)))
+ (t type)))
+ (cons (cond
+ ((eq (car type) 'string) `(vector character ,@(cdr type)))
+ ((eq (car type) 'simple-string)
+ `(simple-array character ,(if (cdr type)
+ (cdr type)
+ '(*))))
+ (t type)))
+ (t type)))
+ (type (specifier-type adjusted-type)))
(cond ((csubtypep type (specifier-type 'list))
- (make-list length :initial-element initial-element))
- ((csubtypep type (specifier-type 'string))
- (if iep
- (make-string length :initial-element initial-element)
- (make-string length)))
- ((csubtypep type (specifier-type 'simple-vector))
- (make-array length :initial-element initial-element))
- ((csubtypep type (specifier-type 'bit-vector))
- (if iep
- (make-array length :element-type '(mod 2)
- :initial-element initial-element)
- (make-array length :element-type '(mod 2))))
- ((csubtypep type (specifier-type 'vector))
- (if (typep type 'array-type)
- (let ((etype (type-specifier
- (array-type-specialized-element-type type)))
- (vlen (car (array-type-dimensions type))))
- (if (and (numberp vlen) (/= vlen length))
- (error 'simple-type-error
- ;; these two are under-specified by ANSI
- :datum (type-specifier type)
- :expected-type (type-specifier type)
- :format-control
- "The length of ~S does not match the specified length of ~S."
- :format-arguments
- (list (type-specifier type) length)))
- (if iep
- (make-array length :element-type etype
- :initial-element initial-element)
- (make-array length :element-type etype)))
- (make-array length :initial-element initial-element)))
- (t (error 'simple-type-error
- :datum type
- :expected-type 'sequence
- :format-control "~S is a bad type specifier for sequences."
- :format-arguments (list type))))))
+ (cond
+ ((type= type (specifier-type 'list))
+ (make-list length :initial-element initial-element))
+ ((eq type *empty-type*)
+ (bad-sequence-type-error nil))
+ ((type= type (specifier-type 'null))
+ (if (= length 0)
+ 'nil
+ (sequence-type-length-mismatch-error type length)))
+ ((cons-type-p type)
+ (multiple-value-bind (min exactp)
+ (sb!kernel::cons-type-length-info type)
+ (if exactp
+ (unless (= length min)
+ (sequence-type-length-mismatch-error type length))
+ (unless (>= length min)
+ (sequence-type-length-mismatch-error type length)))
+ (make-list length :initial-element initial-element)))
+ ;; We'll get here for e.g. (OR NULL (CONS INTEGER *)),
+ ;; which may seem strange and non-ideal, but then I'd say
+ ;; it was stranger to feed that type in to MAKE-SEQUENCE.
+ (t (sequence-type-too-hairy (type-specifier type)))))
+ ((csubtypep type (specifier-type 'vector))
+ (cond
+ (;; is it immediately obvious what the result type is?
+ (typep type 'array-type)
+ (progn
+ (aver (= (length (array-type-dimensions type)) 1))
+ (let* ((etype (type-specifier
+ (array-type-specialized-element-type type)))
+ (etype (if (eq etype '*) t etype))
+ (type-length (car (array-type-dimensions type))))
+ (unless (or (eq type-length '*)
+ (= type-length length))
+ (sequence-type-length-mismatch-error type length))
+ ;; FIXME: These calls to MAKE-ARRAY can't be
+ ;; open-coded, as the :ELEMENT-TYPE argument isn't
+ ;; constant. Probably we ought to write a
+ ;; DEFTRANSFORM for MAKE-SEQUENCE. -- CSR,
+ ;; 2002-07-22
+ (if iep
+ (make-array length :element-type etype
+ :initial-element initial-element)
+ (make-array length :element-type etype)))))
+ (t (sequence-type-too-hairy (type-specifier type)))))
+ ((and (csubtypep type (specifier-type 'sequence))
+ (find-class adjusted-type nil))
+ (let* ((class (find-class adjusted-type nil)))
+ (unless (sb!mop:class-finalized-p class)
+ (sb!mop:finalize-inheritance class))
+ (if iep
+ (sb!sequence:make-sequence-like
+ (sb!mop:class-prototype class) length
+ :initial-element initial-element)
+ (sb!sequence:make-sequence-like
+ (sb!mop:class-prototype class) length))))
+ (t (bad-sequence-type-error (type-specifier type))))))
\f
;;;; SUBSEQ
;;;;
-;;;; The support routines for SUBSEQ are used by compiler transforms, so we
-;;;; worry about dealing with END being supplied or defaulting to NIL
-;;;; at this level.
-
-(defun vector-subseq* (sequence start &optional end)
+;;;; The support routines for SUBSEQ are used by compiler transforms,
+;;;; so we worry about dealing with END being supplied or defaulting
+;;;; to NIL at this level.
+
+(defun string-subseq* (sequence start end)
+ (with-array-data ((data sequence)
+ (start start)
+ (end end)
+ :force-inline t
+ :check-fill-pointer t)
+ (declare (optimize (speed 3) (safety 0)))
+ (string-dispatch ((simple-array character (*))
+ (simple-array base-char (*))
+ (vector nil))
+ data
+ (subseq data start end))))
+
+(defun vector-subseq* (sequence start end)
(declare (type vector sequence))
- (declare (type fixnum start))
- (declare (type (or null fixnum) end))
- (when (null end) (setf end (length sequence)))
- (do ((old-index start (1+ old-index))
- (new-index 0 (1+ new-index))
- (copy (make-sequence-like sequence (- end start))))
- ((= old-index end) copy)
- (declare (fixnum old-index new-index))
- (setf (aref copy new-index) (aref sequence old-index))))
-
-(defun list-subseq* (sequence start &optional end)
- (declare (type list sequence))
- (declare (type fixnum start))
- (declare (type (or null fixnum) end))
- (if (and end (>= start (the fixnum end)))
- ()
- (let* ((groveled (nthcdr start sequence))
- (result (list (car groveled))))
- (if groveled
- (do ((list (cdr groveled) (cdr list))
- (splice result (cdr (rplacd splice (list (car list)))))
- (index (1+ start) (1+ index)))
- ((or (atom list) (and end (= index (the fixnum end))))
- result)
- (declare (fixnum index)))
- ()))))
-
-;;; SUBSEQ cannot default end to the length of sequence since it is not
-;;; an error to supply nil for its value. We must test for end being nil
-;;; in the body of the function, and this is actually done in the support
-;;; routines for other reasons (see above).
+ (declare (type index start)
+ (type (or null index) end))
+ (with-array-data ((data sequence)
+ (start start)
+ (end end)
+ :check-fill-pointer t
+ :force-inline t)
+ (let* ((copy (%make-sequence-like sequence (- end start)))
+ (setter (!find-data-vector-setter copy))
+ (reffer (!find-data-vector-reffer data)))
+ (declare (optimize (speed 3) (safety 0)))
+ (do ((old-index start (1+ old-index))
+ (new-index 0 (1+ new-index)))
+ ((= old-index end) copy)
+ (declare (index old-index new-index))
+ (funcall setter copy new-index
+ (funcall reffer data old-index))))))
+
+(defun list-subseq* (sequence start end)
+ (declare (type list sequence)
+ (type unsigned-byte start)
+ (type (or null unsigned-byte) end))
+ (flet ((oops ()
+ (sequence-bounding-indices-bad-error sequence start end)))
+ (let ((pointer sequence))
+ (unless (zerop start)
+ ;; If START > 0 the list cannot be empty. So CDR down to
+ ;; it START-1 times, check that we still have something, then
+ ;; CDR the final time.
+ ;;
+ ;; If START was zero, the list may be empty if END is NIL or
+ ;; also zero.
+ (when (> start 1)
+ (setf pointer (nthcdr (1- start) pointer)))
+ (if pointer
+ (pop pointer)
+ (oops)))
+ (if end
+ (let ((n (- end start)))
+ (declare (integer n))
+ (when (minusp n)
+ (oops))
+ (when (plusp n)
+ (let* ((head (list nil))
+ (tail head))
+ (macrolet ((pop-one ()
+ `(let ((tmp (list (pop pointer))))
+ (setf (cdr tail) tmp
+ tail tmp))))
+ ;; Bignum case
+ (loop until (fixnump n)
+ do (pop-one)
+ (decf n))
+ ;; Fixnum case, but leave last element, so we should
+ ;; still have something left in the sequence.
+ (let ((m (1- n)))
+ (declare (fixnum m))
+ (loop repeat m
+ do (pop-one)))
+ (unless pointer
+ (oops))
+ ;; OK, pop the last one.
+ (pop-one)
+ (cdr head)))))
+ (loop while pointer
+ collect (pop pointer))))))
+
(defun subseq (sequence start &optional end)
#!+sb-doc
- "Returns a copy of a subsequence of SEQUENCE starting with element number
+ "Return a copy of a subsequence of SEQUENCE starting with element number
START and continuing to the end of SEQUENCE or the optional END."
(seq-dispatch sequence
- (list-subseq* sequence start end)
- (vector-subseq* sequence start end)))
+ (list-subseq* sequence start end)
+ (vector-subseq* sequence start end)
+ (sb!sequence:subseq sequence start end)))
\f
;;;; COPY-SEQ
-(eval-when (:compile-toplevel :execute)
-
-(sb!xc:defmacro vector-copy-seq (sequence type)
- `(let ((length (length (the vector ,sequence))))
- (declare (fixnum length))
- (do ((index 0 (1+ index))
- (copy (make-sequence-of-type ,type length)))
- ((= index length) copy)
- (declare (fixnum index))
- (setf (aref copy index) (aref ,sequence index)))))
-
-(sb!xc:defmacro list-copy-seq (list)
- `(if (atom ,list) '()
- (let ((result (cons (car ,list) '()) ))
- (do ((x (cdr ,list) (cdr x))
- (splice result
- (cdr (rplacd splice (cons (car x) '() ))) ))
- ((atom x) (unless (null x)
- (rplacd splice x))
- result)))))
-
-) ; EVAL-WHEN
-
(defun copy-seq (sequence)
- #!+sb-doc "Returns a copy of SEQUENCE which is EQUAL to SEQUENCE but not EQ."
+ #!+sb-doc "Return a copy of SEQUENCE which is EQUAL to SEQUENCE but not EQ."
(seq-dispatch sequence
- (list-copy-seq* sequence)
- (vector-copy-seq* sequence)))
-
-;;; internal frobs
+ (list-copy-seq* sequence)
+ (vector-subseq* sequence 0 nil)
+ (sb!sequence:copy-seq sequence)))
(defun list-copy-seq* (sequence)
- (list-copy-seq sequence))
-
-(defun vector-copy-seq* (sequence)
- (vector-copy-seq sequence (type-of sequence)))
+ (!copy-list-macro sequence :check-proper-list t))
\f
;;;; FILL
-(eval-when (:compile-toplevel :execute)
-
-(sb!xc:defmacro vector-fill (sequence item start end)
- `(do ((index ,start (1+ index)))
- ((= index (the fixnum ,end)) ,sequence)
- (declare (fixnum index))
- (setf (aref ,sequence index) ,item)))
-
-(sb!xc:defmacro list-fill (sequence item start end)
- `(do ((current (nthcdr ,start ,sequence) (cdr current))
- (index ,start (1+ index)))
- ((or (atom current) (and end (= index (the fixnum ,end))))
- sequence)
- (declare (fixnum index))
- (rplaca current ,item)))
-
-) ; EVAL-WHEN
-
-;;; The support routines for FILL are used by compiler transforms, so we
-;;; worry about dealing with END being supplied or defaulting to NIL
-;;; at this level.
-
(defun list-fill* (sequence item start end)
- (declare (list sequence))
- (list-fill sequence item start end))
+ (declare (type list sequence)
+ (type unsigned-byte start)
+ (type (or null unsigned-byte) end))
+ (flet ((oops ()
+ (sequence-bounding-indices-bad-error sequence start end)))
+ (let ((pointer sequence))
+ (unless (zerop start)
+ ;; If START > 0 the list cannot be empty. So CDR down to it
+ ;; START-1 times, check that we still have something, then CDR
+ ;; the final time.
+ ;;
+ ;; If START was zero, the list may be empty if END is NIL or
+ ;; also zero.
+ (unless (= start 1)
+ (setf pointer (nthcdr (1- start) pointer)))
+ (if pointer
+ (pop pointer)
+ (oops)))
+ (if end
+ (let ((n (- end start)))
+ (declare (integer n))
+ (when (minusp n)
+ (oops))
+ (when (plusp n)
+ (loop repeat n
+ do (setf pointer (cdr (rplaca pointer item))))))
+ (loop while pointer
+ do (setf pointer (cdr (rplaca pointer item)))))))
+ sequence)
(defun vector-fill* (sequence item start end)
- (declare (vector sequence))
- (when (null end) (setq end (length sequence)))
- (vector-fill sequence item start end))
-
-;;; FILL cannot default end to the length of sequence since it is not
-;;; an error to supply nil for its value. We must test for end being nil
-;;; in the body of the function, and this is actually done in the support
-;;; routines for other reasons (see above).
+ (with-array-data ((data sequence)
+ (start start)
+ (end end)
+ :force-inline t
+ :check-fill-pointer t)
+ (let ((setter (!find-data-vector-setter data)))
+ (declare (optimize (speed 3) (safety 0)))
+ (do ((index start (1+ index)))
+ ((= index end) sequence)
+ (declare (index index))
+ (funcall setter data index item)))))
+
+(defun string-fill* (sequence item start end)
+ (declare (string sequence))
+ (with-array-data ((data sequence)
+ (start start)
+ (end end)
+ :force-inline t
+ :check-fill-pointer t)
+ ;; DEFTRANSFORM for FILL will turn these into
+ ;; calls to UB*-BASH-FILL.
+ (etypecase data
+ #!+sb-unicode
+ ((simple-array character (*))
+ (let ((item (locally (declare (optimize (safety 3)))
+ (the character item))))
+ (fill data item :start start :end end)))
+ ((simple-array base-char (*))
+ (let ((item (locally (declare (optimize (safety 3)))
+ (the base-char item))))
+ (fill data item :start start :end end))))))
+
(defun fill (sequence item &key (start 0) end)
- #!+sb-doc "Replace the specified elements of SEQUENCE with ITEM."
+ #!+sb-doc
+ "Replace the specified elements of SEQUENCE with ITEM."
(seq-dispatch sequence
- (list-fill* sequence item start end)
- (vector-fill* sequence item start end)))
+ (list-fill* sequence item start end)
+ (vector-fill* sequence item start end)
+ (sb!sequence:fill sequence item
+ :start start
+ :end (%check-generic-sequence-bounds sequence start end))))
\f
;;;; REPLACE
(sb!xc:defmacro mumble-replace-from-mumble ()
`(if (and (eq target-sequence source-sequence) (> target-start source-start))
(let ((nelts (min (- target-end target-start)
- (- source-end source-start))))
- (do ((target-index (+ (the fixnum target-start) (the fixnum nelts) -1)
- (1- target-index))
- (source-index (+ (the fixnum source-start) (the fixnum nelts) -1)
- (1- source-index)))
- ((= target-index (the fixnum (1- target-start))) target-sequence)
- (declare (fixnum target-index source-index))
- (setf (aref target-sequence target-index)
- (aref source-sequence source-index))))
+ (- source-end source-start))))
+ (do ((target-index (+ (the fixnum target-start) (the fixnum nelts) -1)
+ (1- target-index))
+ (source-index (+ (the fixnum source-start) (the fixnum nelts) -1)
+ (1- source-index)))
+ ((= target-index (the fixnum (1- target-start))) target-sequence)
+ (declare (fixnum target-index source-index))
+ ;; disable bounds checking
+ (declare (optimize (safety 0)))
+ (setf (aref target-sequence target-index)
+ (aref source-sequence source-index))))
(do ((target-index target-start (1+ target-index))
- (source-index source-start (1+ source-index)))
- ((or (= target-index (the fixnum target-end))
- (= source-index (the fixnum source-end)))
- target-sequence)
- (declare (fixnum target-index source-index))
- (setf (aref target-sequence target-index)
- (aref source-sequence source-index)))))
+ (source-index source-start (1+ source-index)))
+ ((or (= target-index (the fixnum target-end))
+ (= source-index (the fixnum source-end)))
+ target-sequence)
+ (declare (fixnum target-index source-index))
+ ;; disable bounds checking
+ (declare (optimize (safety 0)))
+ (setf (aref target-sequence target-index)
+ (aref source-sequence source-index)))))
(sb!xc:defmacro list-replace-from-list ()
`(if (and (eq target-sequence source-sequence) (> target-start source-start))
(let ((new-elts (subseq source-sequence source-start
- (+ (the fixnum source-start)
- (the fixnum
- (min (- (the fixnum target-end)
- (the fixnum target-start))
- (- (the fixnum source-end)
- (the fixnum source-start))))))))
- (do ((n new-elts (cdr n))
- (o (nthcdr target-start target-sequence) (cdr o)))
- ((null n) target-sequence)
- (rplaca o (car n))))
+ (+ (the fixnum source-start)
+ (the fixnum
+ (min (- (the fixnum target-end)
+ (the fixnum target-start))
+ (- (the fixnum source-end)
+ (the fixnum source-start))))))))
+ (do ((n new-elts (cdr n))
+ (o (nthcdr target-start target-sequence) (cdr o)))
+ ((null n) target-sequence)
+ (rplaca o (car n))))
(do ((target-index target-start (1+ target-index))
- (source-index source-start (1+ source-index))
- (target-sequence-ref (nthcdr target-start target-sequence)
- (cdr target-sequence-ref))
- (source-sequence-ref (nthcdr source-start source-sequence)
- (cdr source-sequence-ref)))
- ((or (= target-index (the fixnum target-end))
- (= source-index (the fixnum source-end))
- (null target-sequence-ref) (null source-sequence-ref))
- target-sequence)
- (declare (fixnum target-index source-index))
- (rplaca target-sequence-ref (car source-sequence-ref)))))
+ (source-index source-start (1+ source-index))
+ (target-sequence-ref (nthcdr target-start target-sequence)
+ (cdr target-sequence-ref))
+ (source-sequence-ref (nthcdr source-start source-sequence)
+ (cdr source-sequence-ref)))
+ ((or (= target-index (the fixnum target-end))
+ (= source-index (the fixnum source-end))
+ (null target-sequence-ref) (null source-sequence-ref))
+ target-sequence)
+ (declare (fixnum target-index source-index))
+ (rplaca target-sequence-ref (car source-sequence-ref)))))
(sb!xc:defmacro list-replace-from-mumble ()
`(do ((target-index target-start (1+ target-index))
- (source-index source-start (1+ source-index))
- (target-sequence-ref (nthcdr target-start target-sequence)
- (cdr target-sequence-ref)))
+ (source-index source-start (1+ source-index))
+ (target-sequence-ref (nthcdr target-start target-sequence)
+ (cdr target-sequence-ref)))
((or (= target-index (the fixnum target-end))
- (= source-index (the fixnum source-end))
- (null target-sequence-ref))
- target-sequence)
+ (= source-index (the fixnum source-end))
+ (null target-sequence-ref))
+ target-sequence)
(declare (fixnum source-index target-index))
(rplaca target-sequence-ref (aref source-sequence source-index))))
(sb!xc:defmacro mumble-replace-from-list ()
`(do ((target-index target-start (1+ target-index))
- (source-index source-start (1+ source-index))
- (source-sequence (nthcdr source-start source-sequence)
- (cdr source-sequence)))
+ (source-index source-start (1+ source-index))
+ (source-sequence (nthcdr source-start source-sequence)
+ (cdr source-sequence)))
((or (= target-index (the fixnum target-end))
- (= source-index (the fixnum source-end))
- (null source-sequence))
- target-sequence)
+ (= source-index (the fixnum source-end))
+ (null source-sequence))
+ target-sequence)
(declare (fixnum target-index source-index))
(setf (aref target-sequence target-index) (car source-sequence))))
;;;; at this level.
(defun list-replace-from-list* (target-sequence source-sequence target-start
- target-end source-start source-end)
+ target-end source-start source-end)
(when (null target-end) (setq target-end (length target-sequence)))
(when (null source-end) (setq source-end (length source-sequence)))
(list-replace-from-list))
(defun list-replace-from-vector* (target-sequence source-sequence target-start
- target-end source-start source-end)
+ target-end source-start source-end)
(when (null target-end) (setq target-end (length target-sequence)))
(when (null source-end) (setq source-end (length source-sequence)))
(list-replace-from-mumble))
(defun vector-replace-from-list* (target-sequence source-sequence target-start
- target-end source-start source-end)
+ target-end source-start source-end)
(when (null target-end) (setq target-end (length target-sequence)))
(when (null source-end) (setq source-end (length source-sequence)))
(mumble-replace-from-list))
(defun vector-replace-from-vector* (target-sequence source-sequence
- target-start target-end source-start
- source-end)
+ target-start target-end source-start
+ source-end)
(when (null target-end) (setq target-end (length target-sequence)))
(when (null source-end) (setq source-end (length source-sequence)))
(mumble-replace-from-mumble))
-;;; REPLACE cannot default END arguments to the length of SEQUENCE since it
-;;; is not an error to supply NIL for their values. We must test for ENDs
-;;; being NIL in the body of the function.
-(defun replace (target-sequence source-sequence &key
- ((:start1 target-start) 0)
- ((:end1 target-end))
- ((:start2 source-start) 0)
- ((:end2 source-end)))
+#!+sb-unicode
+(defun simple-character-string-replace-from-simple-character-string*
+ (target-sequence source-sequence
+ target-start target-end source-start source-end)
+ (declare (type (simple-array character (*)) target-sequence source-sequence))
+ (when (null target-end) (setq target-end (length target-sequence)))
+ (when (null source-end) (setq source-end (length source-sequence)))
+ (mumble-replace-from-mumble))
+
+(define-sequence-traverser replace
+ (sequence1 sequence2 &rest args &key start1 end1 start2 end2)
#!+sb-doc
"The target sequence is destructively modified by copying successive
elements into it from the source sequence."
- (let ((target-end (or target-end (length target-sequence)))
- (source-end (or source-end (length source-sequence))))
+ (declare (truly-dynamic-extent args))
+ (let* (;; KLUDGE: absent either rewriting FOO-REPLACE-FROM-BAR, or
+ ;; excessively polluting DEFINE-SEQUENCE-TRAVERSER, we rebind
+ ;; these things here so that legacy code gets the names it's
+ ;; expecting. We could use &AUX instead :-/.
+ (target-sequence sequence1)
+ (source-sequence sequence2)
+ (target-start start1)
+ (source-start start2)
+ (target-end (or end1 length1))
+ (source-end (or end2 length2)))
(seq-dispatch target-sequence
- (seq-dispatch source-sequence
- (list-replace-from-list)
- (list-replace-from-mumble))
- (seq-dispatch source-sequence
- (mumble-replace-from-list)
- (mumble-replace-from-mumble)))))
+ (seq-dispatch source-sequence
+ (list-replace-from-list)
+ (list-replace-from-mumble)
+ (apply #'sb!sequence:replace sequence1 sequence2 args))
+ (seq-dispatch source-sequence
+ (mumble-replace-from-list)
+ (mumble-replace-from-mumble)
+ (apply #'sb!sequence:replace sequence1 sequence2 args))
+ (apply #'sb!sequence:replace sequence1 sequence2 args))))
\f
;;;; REVERSE
(eval-when (:compile-toplevel :execute)
-(sb!xc:defmacro vector-reverse (sequence type)
+(sb!xc:defmacro vector-reverse (sequence)
`(let ((length (length ,sequence)))
(declare (fixnum length))
(do ((forward-index 0 (1+ forward-index))
- (backward-index (1- length) (1- backward-index))
- (new-sequence (make-sequence-of-type ,type length)))
- ((= forward-index length) new-sequence)
+ (backward-index (1- length) (1- backward-index))
+ (new-sequence (%make-sequence-like sequence length)))
+ ((= forward-index length) new-sequence)
(declare (fixnum forward-index backward-index))
(setf (aref new-sequence forward-index)
- (aref ,sequence backward-index)))))
+ (aref ,sequence backward-index)))))
(sb!xc:defmacro list-reverse-macro (sequence)
`(do ((new-list ()))
- ((atom ,sequence) new-list)
+ ((endp ,sequence) new-list)
(push (pop ,sequence) new-list)))
) ; EVAL-WHEN
(defun reverse (sequence)
#!+sb-doc
- "Returns a new sequence containing the same elements but in reverse order."
+ "Return a new sequence containing the same elements but in reverse order."
(seq-dispatch sequence
- (list-reverse* sequence)
- (vector-reverse* sequence)))
+ (list-reverse* sequence)
+ (vector-reverse* sequence)
+ (sb!sequence:reverse sequence)))
;;; internal frobs
(list-reverse-macro sequence))
(defun vector-reverse* (sequence)
- (vector-reverse sequence (type-of sequence)))
+ (vector-reverse sequence))
\f
;;;; NREVERSE
(sb!xc:defmacro vector-nreverse (sequence)
`(let ((length (length (the vector ,sequence))))
- (declare (fixnum length))
- (do ((left-index 0 (1+ left-index))
- (right-index (1- length) (1- right-index))
- (half-length (truncate length 2)))
- ((= left-index half-length) ,sequence)
- (declare (fixnum left-index right-index half-length))
- (rotatef (aref ,sequence left-index)
- (aref ,sequence right-index)))))
+ (when (>= length 2)
+ (do ((left-index 0 (1+ left-index))
+ (right-index (1- length) (1- right-index)))
+ ((<= right-index left-index))
+ (declare (type index left-index right-index))
+ (rotatef (aref ,sequence left-index)
+ (aref ,sequence right-index))))
+ ,sequence))
(sb!xc:defmacro list-nreverse-macro (list)
- `(do ((1st (cdr ,list) (if (atom 1st) 1st (cdr 1st)))
- (2nd ,list 1st)
- (3rd '() 2nd))
+ `(do ((1st (cdr ,list) (if (endp 1st) 1st (cdr 1st)))
+ (2nd ,list 1st)
+ (3rd '() 2nd))
((atom 2nd) 3rd)
(rplacd 2nd 3rd)))
(defun nreverse (sequence)
#!+sb-doc
- "Returns a sequence of the same elements in reverse order; the argument
+ "Return a sequence of the same elements in reverse order; the argument
is destroyed."
(seq-dispatch sequence
- (list-nreverse* sequence)
- (vector-nreverse* sequence)))
+ (list-nreverse* sequence)
+ (vector-nreverse* sequence)
+ (sb!sequence:nreverse sequence)))
\f
;;;; CONCATENATE
-(eval-when (:compile-toplevel :execute)
-
-(sb!xc:defmacro concatenate-to-list (sequences)
- `(let ((result (list nil)))
- (do ((sequences ,sequences (cdr sequences))
- (splice result))
- ((null sequences) (cdr result))
- (let ((sequence (car sequences)))
- ;; FIXME: It appears to me that this and CONCATENATE-TO-MUMBLE
- ;; could benefit from a DO-SEQUENCE macro.
- (seq-dispatch sequence
- (do ((sequence sequence (cdr sequence)))
- ((atom sequence))
- (setq splice
- (cdr (rplacd splice (list (car sequence))))))
- (do ((index 0 (1+ index))
- (length (length sequence)))
- ((= index length))
- (declare (fixnum index length))
- (setq splice
- (cdr (rplacd splice
- (list (aref sequence index)))))))))))
-
-(sb!xc:defmacro concatenate-to-mumble (output-type-spec sequences)
- `(do ((seqs ,sequences (cdr seqs))
- (total-length 0)
- (lengths ()))
- ((null seqs)
- (do ((sequences ,sequences (cdr sequences))
- (lengths lengths (cdr lengths))
- (index 0)
- (result (make-sequence-of-type ,output-type-spec total-length)))
- ((= index total-length) result)
- (declare (fixnum index))
- (let ((sequence (car sequences)))
- (seq-dispatch sequence
- (do ((sequence sequence (cdr sequence)))
- ((atom sequence))
- (setf (aref result index) (car sequence))
- (setq index (1+ index)))
- (do ((jndex 0 (1+ jndex))
- (this-length (car lengths)))
- ((= jndex this-length))
- (declare (fixnum jndex this-length))
- (setf (aref result index)
- (aref sequence jndex))
- (setq index (1+ index)))))))
- (let ((length (length (car seqs))))
- (declare (fixnum length))
- (setq lengths (nconc lengths (list length)))
- (setq total-length (+ total-length length)))))
-
-) ; EVAL-WHEN
+(defmacro sb!sequence:dosequence ((e sequence &optional return) &body body)
+ (multiple-value-bind (forms decls) (parse-body body :doc-string-allowed nil)
+ (let ((s sequence)
+ (sequence (gensym "SEQUENCE")))
+ `(block nil
+ (let ((,sequence ,s))
+ (seq-dispatch ,sequence
+ (dolist (,e ,sequence ,return) ,@body)
+ (dovector (,e ,sequence ,return) ,@body)
+ (multiple-value-bind (state limit from-end step endp elt)
+ (sb!sequence:make-sequence-iterator ,sequence)
+ (do ((state state (funcall step ,sequence state from-end)))
+ ((funcall endp ,sequence state limit from-end)
+ (let ((,e nil))
+ ,@(filter-dolist-declarations decls)
+ ,e
+ ,return))
+ (let ((,e (funcall elt ,sequence state)))
+ ,@decls
+ (tagbody
+ ,@forms))))))))))
\f
-;;; FIXME: Make a compiler macro or transform for this which efficiently
-;;; handles the case of constant 'STRING first argument. (It's not just time
-;;; efficiency, but space efficiency..)
(defun concatenate (output-type-spec &rest sequences)
#!+sb-doc
- "Returns a new sequence of all the argument sequences concatenated together
+ "Return a new sequence of all the argument sequences concatenated together
which shares no structure with the original argument sequences of the
specified OUTPUT-TYPE-SPEC."
- (case (type-specifier-atom output-type-spec)
- ((simple-vector simple-string vector string array simple-array
- bit-vector simple-bit-vector base-string
- simple-base-string) ; FIXME: unifying principle here?
- (let ((result (apply #'concat-to-simple* output-type-spec sequences)))
- #!+high-security
- (check-type-var result output-type-spec)
- result))
- (list (apply #'concat-to-list* sequences))
- (t
- (apply #'concatenate (result-type-or-lose output-type-spec) sequences))))
-
-;;; internal frobs
-;;; FIXME: These are weird. They're never called anywhere except in
-;;; CONCATENATE. It seems to me that the macros ought to just
-;;; be expanded directly in CONCATENATE, or in CONCATENATE-STRING
-;;; and CONCATENATE-LIST variants. Failing that, these ought to be local
-;;; functions (FLET).
-(defun concat-to-list* (&rest sequences)
- (concatenate-to-list sequences))
-(defun concat-to-simple* (type &rest sequences)
- (concatenate-to-mumble type sequences))
+ (flet ((concat-to-list* (sequences)
+ (let ((result (list nil)))
+ (do ((sequences sequences (cdr sequences))
+ (splice result))
+ ((null sequences) (cdr result))
+ (let ((sequence (car sequences)))
+ (sb!sequence:dosequence (e sequence)
+ (setq splice (cdr (rplacd splice (list e)))))))))
+ (concat-to-simple* (type-spec sequences)
+ (do ((seqs sequences (cdr seqs))
+ (total-length 0)
+ (lengths ()))
+ ((null seqs)
+ (do ((sequences sequences (cdr sequences))
+ (lengths lengths (cdr lengths))
+ (index 0)
+ (result (make-sequence type-spec total-length)))
+ ((= index total-length) result)
+ (declare (fixnum index))
+ (let ((sequence (car sequences)))
+ (sb!sequence:dosequence (e sequence)
+ (setf (aref result index) e)
+ (incf index)))))
+ (let ((length (length (car seqs))))
+ (declare (fixnum length))
+ (setq lengths (nconc lengths (list length)))
+ (setq total-length (+ total-length length))))))
+ (let ((type (specifier-type output-type-spec)))
+ (cond
+ ((csubtypep type (specifier-type 'list))
+ (cond
+ ((type= type (specifier-type 'list))
+ (concat-to-list* sequences))
+ ((eq type *empty-type*)
+ (bad-sequence-type-error nil))
+ ((type= type (specifier-type 'null))
+ (if (every (lambda (x) (or (null x)
+ (and (vectorp x) (= (length x) 0))))
+ sequences)
+ 'nil
+ (sequence-type-length-mismatch-error
+ type
+ ;; FIXME: circular list issues.
+ (reduce #'+ sequences :key #'length))))
+ ((cons-type-p type)
+ (multiple-value-bind (min exactp)
+ (sb!kernel::cons-type-length-info type)
+ (let ((length (reduce #'+ sequences :key #'length)))
+ (if exactp
+ (unless (= length min)
+ (sequence-type-length-mismatch-error type length))
+ (unless (>= length min)
+ (sequence-type-length-mismatch-error type length)))
+ (concat-to-list* sequences))))
+ (t (sequence-type-too-hairy (type-specifier type)))))
+ ((csubtypep type (specifier-type 'vector))
+ (concat-to-simple* output-type-spec sequences))
+ ((and (csubtypep type (specifier-type 'sequence))
+ (find-class output-type-spec nil))
+ (coerce (concat-to-simple* 'vector sequences) output-type-spec))
+ (t
+ (bad-sequence-type-error output-type-spec))))))
+
+;;; Efficient out-of-line concatenate for strings. Compiler transforms
+;;; CONCATENATE 'STRING &co into these.
+(macrolet ((def (name element-type)
+ `(defun ,name (&rest sequences)
+ (declare (dynamic-extent sequences)
+ (optimize speed)
+ (optimize (sb!c::insert-array-bounds-checks 0)))
+ (let* ((lengths (mapcar #'length sequences))
+ (result (make-array (the integer (apply #'+ lengths))
+ :element-type ',element-type))
+ (start 0))
+ (declare (index start))
+ (dolist (seq sequences)
+ (string-dispatch
+ ((simple-array character (*))
+ (simple-array base-char (*))
+ t)
+ seq
+ (replace result seq :start1 start))
+ (incf start (the index (pop lengths))))
+ result))))
+ (def %concatenate-to-string character)
+ (def %concatenate-to-base-string base-char))
\f
;;;; MAP and MAP-INTO
;;; helper functions to handle arity-1 subcases of MAP
(declaim (ftype (function (function sequence) list) %map-list-arity-1))
(declaim (ftype (function (function sequence) simple-vector)
- %map-simple-vector-arity-1))
-(macrolet ((dosequence ((i sequence) &body body)
- (once-only ((sequence sequence))
- `(etypecase ,sequence
- (list (dolist (,i ,sequence) ,@body))
- (simple-vector (dovector (,i sequence) ,@body))
- (vector (dovector (,i sequence) ,@body))))))
- (defun %map-to-list-arity-1 (fun sequence)
- (let ((reversed-result nil)
- (really-fun (%coerce-callable-to-function fun)))
- (dosequence (element sequence)
- (push (funcall really-fun element)
- reversed-result))
- (nreverse reversed-result)))
- (defun %map-to-simple-vector-arity-1 (fun sequence)
- (let ((result (make-array (length sequence)))
- (index 0)
- (really-fun (%coerce-callable-to-function fun)))
- (declare (type index index))
- (dosequence (element sequence)
- (setf (aref result index)
- (funcall really-fun element))
- (incf index))
- result))
- (defun %map-for-effect-arity-1 (fun sequence)
- (let ((really-fun (%coerce-callable-to-function fun)))
- (dosequence (element sequence)
- (funcall really-fun element)))
- nil))
-
-;;; helper functions to handle arity-N subcases of MAP
-;;;
-;;; KLUDGE: This is hairier, and larger, than need be, because we
-;;; don't have DYNAMIC-EXTENT. With DYNAMIC-EXTENT, we could define
-;;; %MAP-FOR-EFFECT, and then implement the
-;;; other %MAP-TO-FOO functions reasonably efficiently by passing closures to
-;;; %MAP-FOR-EFFECT. (DYNAMIC-EXTENT would help a little by avoiding
-;;; consing each closure, and would help a lot by allowing us to define
-;;; a closure (LAMBDA (&REST REST) <do something with (APPLY FUN REST)>)
-;;; with the REST list allocated with DYNAMIC-EXTENT. -- WHN 20000920
-(macrolet (;; Execute BODY in a context where the machinery for
- ;; UPDATED-MAP-APPLY-ARGS has been set up.
- (with-map-state (sequences &body body)
- `(let* ((%sequences ,sequences)
- (%iters (mapcar (lambda (sequence)
- (etypecase sequence
- (list sequence)
- (vector 0)))
- %sequences))
- (%apply-args (make-list (length %sequences))))
- (declare (type list %sequences %iters %apply-args))
- ,@body))
- ;; Return a list of args to pass to APPLY for the next
- ;; function call in the mapping, or NIL if no more function
- ;; calls should be made (because we've reached the end of a
- ;; sequence arg).
- (updated-map-apply-args ()
- '(do ((in-sequences %sequences (cdr in-sequences))
- (in-iters %iters (cdr in-iters))
- (in-apply-args %apply-args (cdr in-apply-args)))
- ((null in-sequences)
- %apply-args)
- (declare (type list in-sequences in-iters in-apply-args))
- (let ((i (car in-iters)))
- (declare (type (or list index) i))
- (if (listp i)
- (if (null i) ; if end of this sequence
- (return nil)
- (setf (car in-apply-args) (car i)
- (car in-iters) (cdr i)))
- (let ((v (the vector (car in-sequences))))
- (if (>= i (length v)) ; if end of this sequence
- (return nil)
- (setf (car in-apply-args) (aref v i)
- (car in-iters) (1+ i)))))))))
- (defun %map-to-list (func sequences)
- (declare (type function func))
- (declare (type list sequences))
- (with-map-state sequences
- (loop with updated-map-apply-args
- while (setf updated-map-apply-args (updated-map-apply-args))
- collect (apply func updated-map-apply-args))))
- (defun %map-to-vector (output-type-spec func sequences)
- (declare (type function func))
- (declare (type list sequences))
- (let ((min-len (with-map-state sequences
- (do ((counter 0 (1+ counter)))
- ;; Note: Doing everything in
- ;; UPDATED-MAP-APPLY-ARGS here is somewhat
- ;; wasteful; we even do some extra consing.
- ;; And stepping over every element of
- ;; VECTORs, instead of just grabbing their
- ;; LENGTH, is also wasteful. But it's easy
- ;; and safe. (If you do rewrite it, please
- ;; try to make sure that
- ;; (MAP NIL #'F SOME-CIRCULAR-LIST #(1))
- ;; does the right thing.)
- ((not (updated-map-apply-args))
- counter)
- (declare (type index counter))))))
- (declare (type index min-len))
- (with-map-state sequences
- (let ((result (make-sequence-of-type output-type-spec min-len))
- (index 0))
- (declare (type index index))
- (loop with updated-map-apply-args
- while (setf updated-map-apply-args (updated-map-apply-args))
- do
- (setf (aref result index)
- (apply func updated-map-apply-args))
- (incf index))
- result))))
- (defun %map-for-effect (func sequences)
- (declare (type function func))
- (declare (type list sequences))
- (with-map-state sequences
- (loop with updated-map-apply-args
- while (setf updated-map-apply-args (updated-map-apply-args))
- do
- (apply func updated-map-apply-args))
- nil)))
-
- "FUNCTION must take as many arguments as there are sequences provided.
- The result is a sequence of type OUTPUT-TYPE-SPEC such that element I
- is the result of applying FUNCTION to element I of each of the argument
- sequences."
+ %map-simple-vector-arity-1))
+(defun %map-to-list-arity-1 (fun sequence)
+ (let ((reversed-result nil)
+ (really-fun (%coerce-callable-to-fun fun)))
+ (sb!sequence:dosequence (element sequence)
+ (push (funcall really-fun element)
+ reversed-result))
+ (nreverse reversed-result)))
+(defun %map-to-simple-vector-arity-1 (fun sequence)
+ (let ((result (make-array (length sequence)))
+ (index 0)
+ (really-fun (%coerce-callable-to-fun fun)))
+ (declare (type index index))
+ (sb!sequence:dosequence (element sequence)
+ (setf (aref result index)
+ (funcall really-fun element))
+ (incf index))
+ result))
+(defun %map-for-effect-arity-1 (fun sequence)
+ (let ((really-fun (%coerce-callable-to-fun fun)))
+ (sb!sequence:dosequence (element sequence)
+ (funcall really-fun element)))
+ nil)
+
+(declaim (maybe-inline %map-for-effect))
+(defun %map-for-effect (fun sequences)
+ (declare (type function fun) (type list sequences))
+ (let ((%sequences sequences)
+ (%iters (mapcar (lambda (s)
+ (seq-dispatch s
+ s
+ 0
+ (multiple-value-list
+ (sb!sequence:make-sequence-iterator s))))
+ sequences))
+ (%apply-args (make-list (length sequences))))
+ ;; this is almost efficient (except in the general case where we
+ ;; trampoline to MAKE-SEQUENCE-ITERATOR; if we had DX allocation
+ ;; of MAKE-LIST, the whole of %MAP would be cons-free.
+ (declare (type list %sequences %iters %apply-args))
+ (loop
+ (do ((in-sequences %sequences (cdr in-sequences))
+ (in-iters %iters (cdr in-iters))
+ (in-apply-args %apply-args (cdr in-apply-args)))
+ ((null in-sequences) (apply fun %apply-args))
+ (let ((i (car in-iters)))
+ (declare (type (or list index) i))
+ (cond
+ ((listp (car in-sequences))
+ (if (null i)
+ (return-from %map-for-effect nil)
+ (setf (car in-apply-args) (car i)
+ (car in-iters) (cdr i))))
+ ((typep i 'index)
+ (let ((v (the vector (car in-sequences))))
+ (if (>= i (length v))
+ (return-from %map-for-effect nil)
+ (setf (car in-apply-args) (aref v i)
+ (car in-iters) (1+ i)))))
+ (t
+ (destructuring-bind (state limit from-end step endp elt &rest ignore)
+ i
+ (declare (type function step endp elt)
+ (ignore ignore))
+ (let ((s (car in-sequences)))
+ (if (funcall endp s state limit from-end)
+ (return-from %map-for-effect nil)
+ (progn
+ (setf (car in-apply-args) (funcall elt s state))
+ (setf (caar in-iters) (funcall step s state from-end)))))))))))))
+(defun %map-to-list (fun sequences)
+ (declare (type function fun)
+ (type list sequences))
+ (let ((result nil))
+ (flet ((f (&rest args)
+ (declare (truly-dynamic-extent args))
+ (push (apply fun args) result)))
+ (declare (truly-dynamic-extent #'f))
+ (%map-for-effect #'f sequences))
+ (nreverse result)))
+(defun %map-to-vector (output-type-spec fun sequences)
+ (declare (type function fun)
+ (type list sequences))
+ (let ((min-len 0))
+ (flet ((f (&rest args)
+ (declare (truly-dynamic-extent args))
+ (declare (ignore args))
+ (incf min-len)))
+ (declare (truly-dynamic-extent #'f))
+ (%map-for-effect #'f sequences))
+ (let ((result (make-sequence output-type-spec min-len))
+ (i 0))
+ (declare (type (simple-array * (*)) result))
+ (flet ((f (&rest args)
+ (declare (truly-dynamic-extent args))
+ (setf (aref result i) (apply fun args))
+ (incf i)))
+ (declare (truly-dynamic-extent #'f))
+ (%map-for-effect #'f sequences))
+ result)))
+(defun %map-to-sequence (result-type fun sequences)
+ (declare (type function fun)
+ (type list sequences))
+ (let ((min-len 0))
+ (flet ((f (&rest args)
+ (declare (truly-dynamic-extent args))
+ (declare (ignore args))
+ (incf min-len)))
+ (declare (truly-dynamic-extent #'f))
+ (%map-for-effect #'f sequences))
+ (let ((result (make-sequence result-type min-len)))
+ (multiple-value-bind (state limit from-end step endp elt setelt)
+ (sb!sequence:make-sequence-iterator result)
+ (declare (ignore limit endp elt))
+ (flet ((f (&rest args)
+ (declare (truly-dynamic-extent args))
+ (funcall setelt (apply fun args) result state)
+ (setq state (funcall step result state from-end))))
+ (declare (truly-dynamic-extent #'f))
+ (%map-for-effect #'f sequences)))
+ result)))
;;; %MAP is just MAP without the final just-to-be-sure check that
;;; length of the output sequence matches any length specified
;;; in RESULT-TYPE.
(defun %map (result-type function first-sequence &rest more-sequences)
- (let ((really-function (%coerce-callable-to-function function)))
+ (let ((really-fun (%coerce-callable-to-fun function))
+ (type (specifier-type result-type)))
;; Handle one-argument MAP NIL specially, using ETYPECASE to turn
;; it into something which can be DEFTRANSFORMed away. (It's
;; fairly important to handle this case efficiently, since
;; quantifiers like SOME are transformed into this case, and since
;; there's no consing overhead to dwarf our inefficiency.)
(if (and (null more-sequences)
- (null result-type))
- (%map-for-effect-arity-1 really-function first-sequence)
- ;; Otherwise, use the industrial-strength full-generality
- ;; approach, consing O(N-ARGS) temporary storage (which can have
- ;; DYNAMIC-EXTENT), then using O(N-ARGS * RESULT-LENGTH) time.
- (let ((sequences (cons first-sequence more-sequences)))
- (case (type-specifier-atom result-type)
- ((nil) (%map-for-effect really-function sequences))
- (list (%map-to-list really-function sequences))
- ((simple-vector simple-string vector string array simple-array
- bit-vector simple-bit-vector base-string simple-base-string)
- (%map-to-vector result-type really-function sequences))
- (t
- (apply #'map
- (result-type-or-lose result-type t)
- really-function
- sequences)))))))
+ (null result-type))
+ (%map-for-effect-arity-1 really-fun first-sequence)
+ ;; Otherwise, use the industrial-strength full-generality
+ ;; approach, consing O(N-ARGS) temporary storage (which can have
+ ;; DYNAMIC-EXTENT), then using O(N-ARGS * RESULT-LENGTH) time.
+ (let ((sequences (cons first-sequence more-sequences)))
+ (cond
+ ((eq type *empty-type*) (%map-for-effect really-fun sequences))
+ ((csubtypep type (specifier-type 'list))
+ (%map-to-list really-fun sequences))
+ ((csubtypep type (specifier-type 'vector))
+ (%map-to-vector result-type really-fun sequences))
+ ((and (csubtypep type (specifier-type 'sequence))
+ (find-class result-type nil))
+ (%map-to-sequence result-type really-fun sequences))
+ (t
+ (bad-sequence-type-error result-type)))))))
(defun map (result-type function first-sequence &rest more-sequences)
- (sequence-of-checked-length-given-type (apply #'%map
- result-type
- function
- first-sequence
- more-sequences)
- ;; (The RESULT-TYPE isn't
- ;; strictly the type of the
- ;; result, because when
- ;; RESULT-TYPE=NIL, the result
- ;; actually has NULL type. But
- ;; that special case doesn't
- ;; matter here, since we only
- ;; look closely at vector
- ;; types; so we can just pass
- ;; RESULT-TYPE straight through
- ;; as a type specifier.)
- result-type))
+ (apply #'%map
+ result-type
+ function
+ first-sequence
+ more-sequences))
;;; KLUDGE: MAP has been rewritten substantially since the fork from
;;; CMU CL in order to give reasonable performance, but this
;;; of (MAP NIL ..). -- WHN 20000920
(defun map-into (result-sequence function &rest sequences)
(let* ((fp-result
- (and (arrayp result-sequence)
- (array-has-fill-pointer-p result-sequence)))
- (len (apply #'min
- (if fp-result
- (array-dimension result-sequence 0)
- (length result-sequence))
- (mapcar #'length sequences))))
+ (and (arrayp result-sequence)
+ (array-has-fill-pointer-p result-sequence)))
+ (len (apply #'min
+ (if fp-result
+ (array-dimension result-sequence 0)
+ (length result-sequence))
+ (mapcar #'length sequences))))
(when fp-result
(setf (fill-pointer result-sequence) len))
- (let ((really-fun (%coerce-callable-to-function function)))
+ (let ((really-fun (%coerce-callable-to-fun function)))
(dotimes (index len)
- (setf (elt result-sequence index)
- (apply really-fun
- (mapcar #'(lambda (seq) (elt seq index))
- sequences))))))
+ (setf (elt result-sequence index)
+ (apply really-fun
+ (mapcar (lambda (seq) (elt seq index))
+ sequences))))))
result-sequence)
\f
;;;; quantifiers
;;; arbitrary sequence arguments, both in the full call case and in
;;; the open code case.
(macrolet ((defquantifier (name found-test found-result
- &key doc (unfound-result (not found-result)))
- `(progn
- ;; KLUDGE: It would be really nice if we could simply
- ;; do something like this
- ;; (declaim (inline ,name))
- ;; (defun ,name (pred first-seq &rest more-seqs)
- ;; ,doc
- ;; (flet ((map-me (&rest rest)
- ;; (let ((pred-value (apply pred rest)))
- ;; (,found-test pred-value
- ;; (return-from ,name
- ;; ,found-result)))))
- ;; (declare (inline map-me))
- ;; (apply #'map nil #'map-me first-seq more-seqs)
- ;; ,unfound-result))
- ;; but Python doesn't seem to be smart enough about
- ;; inlining and APPLY to recognize that it can use
- ;; the DEFTRANSFORM for MAP in the resulting inline
- ;; expansion. I don't have any appetite for deep
- ;; compiler hacking right now, so I'll just work
- ;; around the apparent problem by using a compiler
- ;; macro instead. -- WHN 20000410
- (defun ,name (pred first-seq &rest more-seqs)
- #!+sb-doc ,doc
- (flet ((map-me (&rest rest)
- (let ((pred-value (apply pred rest)))
- (,found-test pred-value
- (return-from ,name
- ,found-result)))))
- (declare (inline map-me))
- (apply #'map nil #'map-me first-seq more-seqs)
- ,unfound-result))
- ;; KLUDGE: It would be more obviously correct -- but
- ;; also significantly messier -- for PRED-VALUE to be
- ;; a gensym. However, a private symbol really does
- ;; seem to be good enough; and anyway the really
- ;; obviously correct solution is to make Python smart
- ;; enough that we can use an inline function instead
- ;; of a compiler macro (as above). -- WHN 20000410
- (define-compiler-macro ,name (pred first-seq &rest more-seqs)
- (let ((elements (make-gensym-list (1+ (length more-seqs))))
- (blockname (gensym "BLOCK")))
- (once-only ((pred pred))
- `(block ,blockname
- (map nil
- (lambda (,@elements)
- (let ((pred-value (funcall ,pred ,@elements)))
- (,',found-test pred-value
- (return-from ,blockname
- ,',found-result))))
- ,first-seq
- ,@more-seqs)
- ,',unfound-result)))))))
+ &key doc (unfound-result (not found-result)))
+ `(progn
+ ;; KLUDGE: It would be really nice if we could simply
+ ;; do something like this
+ ;; (declaim (inline ,name))
+ ;; (defun ,name (pred first-seq &rest more-seqs)
+ ;; ,doc
+ ;; (flet ((map-me (&rest rest)
+ ;; (let ((pred-value (apply pred rest)))
+ ;; (,found-test pred-value
+ ;; (return-from ,name
+ ;; ,found-result)))))
+ ;; (declare (inline map-me))
+ ;; (apply #'map nil #'map-me first-seq more-seqs)
+ ;; ,unfound-result))
+ ;; but Python doesn't seem to be smart enough about
+ ;; inlining and APPLY to recognize that it can use
+ ;; the DEFTRANSFORM for MAP in the resulting inline
+ ;; expansion. I don't have any appetite for deep
+ ;; compiler hacking right now, so I'll just work
+ ;; around the apparent problem by using a compiler
+ ;; macro instead. -- WHN 20000410
+ (defun ,name (pred first-seq &rest more-seqs)
+ #!+sb-doc ,doc
+ (flet ((map-me (&rest rest)
+ (let ((pred-value (apply pred rest)))
+ (,found-test pred-value
+ (return-from ,name
+ ,found-result)))))
+ (declare (inline map-me))
+ (apply #'map nil #'map-me first-seq more-seqs)
+ ,unfound-result))
+ ;; KLUDGE: It would be more obviously correct -- but
+ ;; also significantly messier -- for PRED-VALUE to be
+ ;; a gensym. However, a private symbol really does
+ ;; seem to be good enough; and anyway the really
+ ;; obviously correct solution is to make Python smart
+ ;; enough that we can use an inline function instead
+ ;; of a compiler macro (as above). -- WHN 20000410
+ ;;
+ ;; FIXME: The DEFINE-COMPILER-MACRO here can be
+ ;; important for performance, and it'd be good to have
+ ;; it be visible throughout the compilation of all the
+ ;; target SBCL code. That could be done by defining
+ ;; SB-XC:DEFINE-COMPILER-MACRO and using it here,
+ ;; moving this DEFQUANTIFIER stuff (and perhaps other
+ ;; inline definitions in seq.lisp as well) into a new
+ ;; seq.lisp, and moving remaining target-only stuff
+ ;; from the old seq.lisp into target-seq.lisp.
+ (define-compiler-macro ,name (pred first-seq &rest more-seqs)
+ (let ((elements (make-gensym-list (1+ (length more-seqs))))
+ (blockname (gensym "BLOCK")))
+ (once-only ((pred pred))
+ `(block ,blockname
+ (map nil
+ (lambda (,@elements)
+ (let ((pred-value (funcall ,pred ,@elements)))
+ (,',found-test pred-value
+ (return-from ,blockname
+ ,',found-result))))
+ ,first-seq
+ ,@more-seqs)
+ ,',unfound-result)))))))
(defquantifier some when pred-value :unfound-result nil :doc
- "PREDICATE is applied to the elements with index 0 of the sequences, then
- possibly to those with index 1, and so on. SOME returns the first
- non-NIL value encountered, or NIL if the end of a sequence is reached.")
+ "Apply PREDICATE to the 0-indexed elements of the sequences, then
+ possibly to those with index 1, and so on. Return the first
+ non-NIL value encountered, or NIL if the end of any sequence is reached.")
(defquantifier every unless nil :doc
- "PREDICATE is applied to the elements with index 0 of the sequences, then
- possibly to those with index 1, and so on. EVERY returns NIL as soon
+ "Apply PREDICATE to the 0-indexed elements of the sequences, then
+ possibly to those with index 1, and so on. Return NIL as soon
as any invocation of PREDICATE returns NIL, or T if every invocation
is non-NIL.")
(defquantifier notany when nil :doc
- "PREDICATE is applied to the elements with index 0 of the sequences, then
- possibly to those with index 1, and so on. NOTANY returns NIL as soon
+ "Apply PREDICATE to the 0-indexed elements of the sequences, then
+ possibly to those with index 1, and so on. Return NIL as soon
as any invocation of PREDICATE returns a non-NIL value, or T if the end
- of a sequence is reached.")
+ of any sequence is reached.")
(defquantifier notevery unless t :doc
- "PREDICATE is applied to the elements with index 0 of the sequences, then
- possibly to those with index 1, and so on. NOTEVERY returns T as soon
+ "Apply PREDICATE to 0-indexed elements of the sequences, then
+ possibly to those with index 1, and so on. Return T as soon
as any invocation of PREDICATE returns NIL, or NIL if every invocation
is non-NIL."))
\f
(eval-when (:compile-toplevel :execute)
(sb!xc:defmacro mumble-reduce (function
- sequence
- key
- start
- end
- initial-value
- ref)
+ sequence
+ key
+ start
+ end
+ initial-value
+ ref)
`(do ((index ,start (1+ index))
- (value ,initial-value))
- ((= index (the fixnum ,end)) value)
- (declare (fixnum index))
+ (value ,initial-value))
+ ((>= index ,end) value)
(setq value (funcall ,function value
- (apply-key ,key (,ref ,sequence index))))))
+ (apply-key ,key (,ref ,sequence index))))))
(sb!xc:defmacro mumble-reduce-from-end (function
- sequence
- key
- start
- end
- initial-value
- ref)
+ sequence
+ key
+ start
+ end
+ initial-value
+ ref)
`(do ((index (1- ,end) (1- index))
- (value ,initial-value)
- (terminus (1- ,start)))
- ((= index terminus) value)
- (declare (fixnum index terminus))
+ (value ,initial-value)
+ (terminus (1- ,start)))
+ ((<= index terminus) value)
(setq value (funcall ,function
- (apply-key ,key (,ref ,sequence index))
- value))))
+ (apply-key ,key (,ref ,sequence index))
+ value))))
(sb!xc:defmacro list-reduce (function
- sequence
- key
- start
- end
- initial-value
- ivp)
+ sequence
+ key
+ start
+ end
+ initial-value
+ ivp)
`(let ((sequence (nthcdr ,start ,sequence)))
- (do ((count (if ,ivp ,start (1+ (the fixnum ,start)))
- (1+ count))
- (sequence (if ,ivp sequence (cdr sequence))
- (cdr sequence))
- (value (if ,ivp ,initial-value (apply-key ,key (car sequence)))
- (funcall ,function value (apply-key ,key (car sequence)))))
- ((= count (the fixnum ,end)) value)
- (declare (fixnum count)))))
+ (do ((count (if ,ivp ,start (1+ ,start))
+ (1+ count))
+ (sequence (if ,ivp sequence (cdr sequence))
+ (cdr sequence))
+ (value (if ,ivp ,initial-value (apply-key ,key (car sequence)))
+ (funcall ,function value (apply-key ,key (car sequence)))))
+ ((>= count ,end) value))))
(sb!xc:defmacro list-reduce-from-end (function
- sequence
- key
- start
- end
- initial-value
- ivp)
- `(let ((sequence (nthcdr (- (the fixnum (length ,sequence))
- (the fixnum ,end))
- (reverse ,sequence))))
- (do ((count (if ,ivp ,start (1+ (the fixnum ,start)))
- (1+ count))
- (sequence (if ,ivp sequence (cdr sequence))
- (cdr sequence))
- (value (if ,ivp ,initial-value (apply-key ,key (car sequence)))
- (funcall ,function (apply-key ,key (car sequence)) value)))
- ((= count (the fixnum ,end)) value)
- (declare (fixnum count)))))
+ sequence
+ key
+ start
+ end
+ initial-value
+ ivp)
+ `(let ((sequence (nthcdr (- (length ,sequence) ,end)
+ (reverse ,sequence))))
+ (do ((count (if ,ivp ,start (1+ ,start))
+ (1+ count))
+ (sequence (if ,ivp sequence (cdr sequence))
+ (cdr sequence))
+ (value (if ,ivp ,initial-value (apply-key ,key (car sequence)))
+ (funcall ,function (apply-key ,key (car sequence)) value)))
+ ((>= count ,end) value))))
) ; EVAL-WHEN
-(defun reduce (function sequence &key key from-end (start 0)
- end (initial-value nil ivp))
+(define-sequence-traverser reduce (function sequence &rest args &key key
+ from-end start end (initial-value nil ivp))
(declare (type index start))
+ (declare (truly-dynamic-extent args))
(let ((start start)
- (end (or end (length sequence))))
+ (end (or end length)))
(declare (type index start end))
- (cond ((= end start)
- (if ivp initial-value (funcall function)))
- ((listp sequence)
- (if from-end
- (list-reduce-from-end function sequence key start end
- initial-value ivp)
- (list-reduce function sequence key start end
- initial-value ivp)))
- (from-end
- (when (not ivp)
- (setq end (1- (the fixnum end)))
- (setq initial-value (apply-key key (aref sequence end))))
- (mumble-reduce-from-end function sequence key start end
- initial-value aref))
- (t
- (when (not ivp)
- (setq initial-value (apply-key key (aref sequence start)))
- (setq start (1+ start)))
- (mumble-reduce function sequence key start end
- initial-value aref)))))
+ (seq-dispatch sequence
+ (if (= end start)
+ (if ivp initial-value (funcall function))
+ (if from-end
+ (list-reduce-from-end function sequence key start end
+ initial-value ivp)
+ (list-reduce function sequence key start end
+ initial-value ivp)))
+ (if (= end start)
+ (if ivp initial-value (funcall function))
+ (if from-end
+ (progn
+ (when (not ivp)
+ (setq end (1- (the fixnum end)))
+ (setq initial-value (apply-key key (aref sequence end))))
+ (mumble-reduce-from-end function sequence key start end
+ initial-value aref))
+ (progn
+ (when (not ivp)
+ (setq initial-value (apply-key key (aref sequence start)))
+ (setq start (1+ start)))
+ (mumble-reduce function sequence key start end
+ initial-value aref))))
+ (apply #'sb!sequence:reduce function sequence args))))
\f
;;;; DELETE
(sb!xc:defmacro mumble-delete (pred)
`(do ((index start (1+ index))
- (jndex start)
- (number-zapped 0))
- ((or (= index (the fixnum end)) (= number-zapped (the fixnum count)))
- (do ((index index (1+ index)) ; Copy the rest of the vector.
- (jndex jndex (1+ jndex)))
- ((= index (the fixnum length))
- (shrink-vector sequence jndex))
- (declare (fixnum index jndex))
- (setf (aref sequence jndex) (aref sequence index))))
+ (jndex start)
+ (number-zapped 0))
+ ((or (= index (the fixnum end)) (= number-zapped count))
+ (do ((index index (1+ index)) ; Copy the rest of the vector.
+ (jndex jndex (1+ jndex)))
+ ((= index (the fixnum length))
+ (shrink-vector sequence jndex))
+ (declare (fixnum index jndex))
+ (setf (aref sequence jndex) (aref sequence index))))
(declare (fixnum index jndex number-zapped))
(setf (aref sequence jndex) (aref sequence index))
(if ,pred
- (setq number-zapped (1+ number-zapped))
- (setq jndex (1+ jndex)))))
+ (incf number-zapped)
+ (incf jndex))))
(sb!xc:defmacro mumble-delete-from-end (pred)
`(do ((index (1- (the fixnum end)) (1- index)) ; Find the losers.
- (number-zapped 0)
- (losers ())
- this-element
- (terminus (1- start)))
- ((or (= index terminus) (= number-zapped (the fixnum count)))
- (do ((losers losers) ; Delete the losers.
- (index start (1+ index))
- (jndex start))
- ((or (null losers) (= index (the fixnum end)))
- (do ((index index (1+ index)) ; Copy the rest of the vector.
- (jndex jndex (1+ jndex)))
- ((= index (the fixnum length))
- (shrink-vector sequence jndex))
- (declare (fixnum index jndex))
- (setf (aref sequence jndex) (aref sequence index))))
- (declare (fixnum index jndex))
- (setf (aref sequence jndex) (aref sequence index))
- (if (= index (the fixnum (car losers)))
- (pop losers)
- (setq jndex (1+ jndex)))))
+ (number-zapped 0)
+ (losers ())
+ this-element
+ (terminus (1- start)))
+ ((or (= index terminus) (= number-zapped count))
+ (do ((losers losers) ; Delete the losers.
+ (index start (1+ index))
+ (jndex start))
+ ((or (null losers) (= index (the fixnum end)))
+ (do ((index index (1+ index)) ; Copy the rest of the vector.
+ (jndex jndex (1+ jndex)))
+ ((= index (the fixnum length))
+ (shrink-vector sequence jndex))
+ (declare (fixnum index jndex))
+ (setf (aref sequence jndex) (aref sequence index))))
+ (declare (fixnum index jndex))
+ (setf (aref sequence jndex) (aref sequence index))
+ (if (= index (the fixnum (car losers)))
+ (pop losers)
+ (incf jndex))))
(declare (fixnum index number-zapped terminus))
(setq this-element (aref sequence index))
(when ,pred
- (setq number-zapped (1+ number-zapped))
+ (incf number-zapped)
(push index losers))))
(sb!xc:defmacro normal-mumble-delete ()
`(mumble-delete
(if test-not
- (not (funcall test-not item (apply-key key (aref sequence index))))
- (funcall test item (apply-key key (aref sequence index))))))
+ (not (funcall test-not item (apply-key key (aref sequence index))))
+ (funcall test item (apply-key key (aref sequence index))))))
(sb!xc:defmacro normal-mumble-delete-from-end ()
`(mumble-delete-from-end
(if test-not
- (not (funcall test-not item (apply-key key this-element)))
- (funcall test item (apply-key key this-element)))))
+ (not (funcall test-not item (apply-key key this-element)))
+ (funcall test item (apply-key key this-element)))))
(sb!xc:defmacro list-delete (pred)
`(let ((handle (cons nil sequence)))
(do ((current (nthcdr start sequence) (cdr current))
- (previous (nthcdr start handle))
- (index start (1+ index))
- (number-zapped 0))
- ((or (= index (the fixnum end)) (= number-zapped (the fixnum count)))
- (cdr handle))
+ (previous (nthcdr start handle))
+ (index start (1+ index))
+ (number-zapped 0))
+ ((or (= index (the fixnum end)) (= number-zapped count))
+ (cdr handle))
(declare (fixnum index number-zapped))
(cond (,pred
- (rplacd previous (cdr current))
- (setq number-zapped (1+ number-zapped)))
- (t
- (setq previous (cdr previous)))))))
+ (rplacd previous (cdr current))
+ (incf number-zapped))
+ (t
+ (setq previous (cdr previous)))))))
(sb!xc:defmacro list-delete-from-end (pred)
`(let* ((reverse (nreverse (the list sequence)))
- (handle (cons nil reverse)))
+ (handle (cons nil reverse)))
(do ((current (nthcdr (- (the fixnum length) (the fixnum end)) reverse)
- (cdr current))
- (previous (nthcdr (- (the fixnum length) (the fixnum end)) handle))
- (index start (1+ index))
- (number-zapped 0))
- ((or (= index (the fixnum end)) (= number-zapped (the fixnum count)))
- (nreverse (cdr handle)))
+ (cdr current))
+ (previous (nthcdr (- (the fixnum length) (the fixnum end)) handle))
+ (index start (1+ index))
+ (number-zapped 0))
+ ((or (= index (the fixnum end)) (= number-zapped count))
+ (nreverse (cdr handle)))
(declare (fixnum index number-zapped))
(cond (,pred
- (rplacd previous (cdr current))
- (setq number-zapped (1+ number-zapped)))
- (t
- (setq previous (cdr previous)))))))
+ (rplacd previous (cdr current))
+ (incf number-zapped))
+ (t
+ (setq previous (cdr previous)))))))
(sb!xc:defmacro normal-list-delete ()
'(list-delete
(if test-not
- (not (funcall test-not item (apply-key key (car current))))
- (funcall test item (apply-key key (car current))))))
+ (not (funcall test-not item (apply-key key (car current))))
+ (funcall test item (apply-key key (car current))))))
(sb!xc:defmacro normal-list-delete-from-end ()
'(list-delete-from-end
(if test-not
- (not (funcall test-not item (apply-key key (car current))))
- (funcall test item (apply-key key (car current))))))
+ (not (funcall test-not item (apply-key key (car current))))
+ (funcall test item (apply-key key (car current))))))
) ; EVAL-WHEN
-(defun delete (item sequence &key from-end (test #'eql) test-not (start 0)
- end count key)
+(define-sequence-traverser delete
+ (item sequence &rest args &key from-end test test-not start
+ end count key)
#!+sb-doc
- "Returns a sequence formed by destructively removing the specified Item from
- the given Sequence."
+ "Return a sequence formed by destructively removing the specified ITEM from
+ the given SEQUENCE."
(declare (fixnum start))
- (let* ((length (length sequence))
- (end (or end length))
- (count (or count most-positive-fixnum)))
- (declare (type index length end)
- (fixnum count))
+ (declare (truly-dynamic-extent args))
+ (let ((end (or end length)))
+ (declare (type index end))
(seq-dispatch sequence
- (if from-end
- (normal-list-delete-from-end)
- (normal-list-delete))
- (if from-end
- (normal-mumble-delete-from-end)
- (normal-mumble-delete)))))
+ (if from-end
+ (normal-list-delete-from-end)
+ (normal-list-delete))
+ (if from-end
+ (normal-mumble-delete-from-end)
+ (normal-mumble-delete))
+ (apply #'sb!sequence:delete item sequence args))))
(eval-when (:compile-toplevel :execute)
) ; EVAL-WHEN
-(defun delete-if (predicate sequence &key from-end (start 0) key end count)
+(define-sequence-traverser delete-if
+ (predicate sequence &rest args &key from-end start key end count)
#!+sb-doc
- "Returns a sequence formed by destructively removing the elements satisfying
- the specified Predicate from the given Sequence."
+ "Return a sequence formed by destructively removing the elements satisfying
+ the specified PREDICATE from the given SEQUENCE."
(declare (fixnum start))
- (let* ((length (length sequence))
- (end (or end length))
- (count (or count most-positive-fixnum)))
- (declare (type index length end)
- (fixnum count))
+ (declare (truly-dynamic-extent args))
+ (let ((end (or end length)))
+ (declare (type index end))
(seq-dispatch sequence
- (if from-end
- (if-list-delete-from-end)
- (if-list-delete))
- (if from-end
- (if-mumble-delete-from-end)
- (if-mumble-delete)))))
+ (if from-end
+ (if-list-delete-from-end)
+ (if-list-delete))
+ (if from-end
+ (if-mumble-delete-from-end)
+ (if-mumble-delete))
+ (apply #'sb!sequence:delete-if predicate sequence args))))
(eval-when (:compile-toplevel :execute)
) ; EVAL-WHEN
-(defun delete-if-not (predicate sequence &key from-end (start 0) end key count)
+(define-sequence-traverser delete-if-not
+ (predicate sequence &rest args &key from-end start end key count)
#!+sb-doc
- "Returns a sequence formed by destructively removing the elements not
- satisfying the specified Predicate from the given Sequence."
+ "Return a sequence formed by destructively removing the elements not
+ satisfying the specified PREDICATE from the given SEQUENCE."
(declare (fixnum start))
- (let* ((length (length sequence))
- (end (or end length))
- (count (or count most-positive-fixnum)))
- (declare (type index length end)
- (fixnum count))
+ (declare (truly-dynamic-extent args))
+ (let ((end (or end length)))
+ (declare (type index end))
(seq-dispatch sequence
- (if from-end
- (if-not-list-delete-from-end)
- (if-not-list-delete))
- (if from-end
- (if-not-mumble-delete-from-end)
- (if-not-mumble-delete)))))
+ (if from-end
+ (if-not-list-delete-from-end)
+ (if-not-list-delete))
+ (if from-end
+ (if-not-mumble-delete-from-end)
+ (if-not-mumble-delete))
+ (apply #'sb!sequence:delete-if-not predicate sequence args))))
\f
;;;; REMOVE
;;; satisfies the predicate.
(sb!xc:defmacro mumble-remove-macro (bump left begin finish right pred)
`(do ((index ,begin (,bump index))
- (result
- (do ((index ,left (,bump index))
- (result (make-sequence-like sequence length)))
- ((= index (the fixnum ,begin)) result)
- (declare (fixnum index))
- (setf (aref result index) (aref sequence index))))
- (new-index ,begin)
- (number-zapped 0)
- (this-element))
+ (result
+ (do ((index ,left (,bump index))
+ (result (%make-sequence-like sequence length)))
+ ((= index (the fixnum ,begin)) result)
+ (declare (fixnum index))
+ (setf (aref result index) (aref sequence index))))
+ (new-index ,begin)
+ (number-zapped 0)
+ (this-element))
((or (= index (the fixnum ,finish))
- (= number-zapped (the fixnum count)))
- (do ((index index (,bump index))
- (new-index new-index (,bump new-index)))
- ((= index (the fixnum ,right)) (shrink-vector result new-index))
- (declare (fixnum index new-index))
- (setf (aref result new-index) (aref sequence index))))
+ (= number-zapped count))
+ (do ((index index (,bump index))
+ (new-index new-index (,bump new-index)))
+ ((= index (the fixnum ,right)) (%shrink-vector result new-index))
+ (declare (fixnum index new-index))
+ (setf (aref result new-index) (aref sequence index))))
(declare (fixnum index new-index number-zapped))
(setq this-element (aref sequence index))
- (cond (,pred (setq number-zapped (1+ number-zapped)))
- (t (setf (aref result new-index) this-element)
- (setq new-index (,bump new-index))))))
+ (cond (,pred (incf number-zapped))
+ (t (setf (aref result new-index) this-element)
+ (setq new-index (,bump new-index))))))
(sb!xc:defmacro mumble-remove (pred)
`(mumble-remove-macro 1+ 0 start end length ,pred))
(sb!xc:defmacro normal-mumble-remove ()
`(mumble-remove
(if test-not
- (not (funcall test-not item (apply-key key this-element)))
- (funcall test item (apply-key key this-element)))))
+ (not (funcall test-not item (apply-key key this-element)))
+ (funcall test item (apply-key key this-element)))))
(sb!xc:defmacro normal-mumble-remove-from-end ()
`(mumble-remove-from-end
(if test-not
- (not (funcall test-not item (apply-key key this-element)))
- (funcall test item (apply-key key this-element)))))
+ (not (funcall test-not item (apply-key key this-element)))
+ (funcall test item (apply-key key this-element)))))
(sb!xc:defmacro if-mumble-remove ()
`(mumble-remove (funcall predicate (apply-key key this-element))))
;;; the predicate.
(sb!xc:defmacro list-remove-macro (pred reverse?)
`(let* ((sequence ,(if reverse?
- '(reverse (the list sequence))
- 'sequence))
- (splice (list nil))
- (results (do ((index 0 (1+ index))
- (before-start splice))
- ((= index (the fixnum start)) before-start)
- (declare (fixnum index))
- (setq splice
- (cdr (rplacd splice (list (pop sequence))))))))
- (do ((index start (1+ index))
- (this-element)
- (number-zapped 0))
- ((or (= index (the fixnum end)) (= number-zapped (the fixnum count)))
- (do ((index index (1+ index)))
- ((null sequence)
- ,(if reverse?
- '(nreverse (the list (cdr results)))
- '(cdr results)))
- (declare (fixnum index))
- (setq splice (cdr (rplacd splice (list (pop sequence)))))))
+ '(reverse (the list sequence))
+ 'sequence))
+ (%start ,(if reverse? '(- length end) 'start))
+ (%end ,(if reverse? '(- length start) 'end))
+ (splice (list nil))
+ (results (do ((index 0 (1+ index))
+ (before-start splice))
+ ((= index (the fixnum %start)) before-start)
+ (declare (fixnum index))
+ (setq splice
+ (cdr (rplacd splice (list (pop sequence))))))))
+ (do ((index %start (1+ index))
+ (this-element)
+ (number-zapped 0))
+ ((or (= index (the fixnum %end)) (= number-zapped count))
+ (do ((index index (1+ index)))
+ ((null sequence)
+ ,(if reverse?
+ '(nreverse (the list (cdr results)))
+ '(cdr results)))
+ (declare (fixnum index))
+ (setq splice (cdr (rplacd splice (list (pop sequence)))))))
(declare (fixnum index number-zapped))
(setq this-element (pop sequence))
(if ,pred
- (setq number-zapped (1+ number-zapped))
- (setq splice (cdr (rplacd splice (list this-element))))))))
+ (setq number-zapped (1+ number-zapped))
+ (setq splice (cdr (rplacd splice (list this-element))))))))
(sb!xc:defmacro list-remove (pred)
`(list-remove-macro ,pred nil))
(sb!xc:defmacro normal-list-remove ()
`(list-remove
(if test-not
- (not (funcall test-not item (apply-key key this-element)))
- (funcall test item (apply-key key this-element)))))
+ (not (funcall test-not item (apply-key key this-element)))
+ (funcall test item (apply-key key this-element)))))
(sb!xc:defmacro normal-list-remove-from-end ()
`(list-remove-from-end
(if test-not
- (not (funcall test-not item (apply-key key this-element)))
- (funcall test item (apply-key key this-element)))))
+ (not (funcall test-not item (apply-key key this-element)))
+ (funcall test item (apply-key key this-element)))))
(sb!xc:defmacro if-list-remove ()
`(list-remove
) ; EVAL-WHEN
-(defun remove (item sequence &key from-end (test #'eql) test-not (start 0)
- end count key)
+(define-sequence-traverser remove
+ (item sequence &rest args &key from-end test test-not start
+ end count key)
#!+sb-doc
- "Returns a copy of SEQUENCE with elements satisfying the test (default is
+ "Return a copy of SEQUENCE with elements satisfying the test (default is
EQL) with ITEM removed."
(declare (fixnum start))
- (let* ((length (length sequence))
- (end (or end length))
- (count (or count most-positive-fixnum)))
- (declare (type index length end)
- (fixnum count))
+ (declare (truly-dynamic-extent args))
+ (let ((end (or end length)))
+ (declare (type index end))
(seq-dispatch sequence
- (if from-end
- (normal-list-remove-from-end)
- (normal-list-remove))
- (if from-end
- (normal-mumble-remove-from-end)
- (normal-mumble-remove)))))
-
-(defun remove-if (predicate sequence &key from-end (start 0) end count key)
+ (if from-end
+ (normal-list-remove-from-end)
+ (normal-list-remove))
+ (if from-end
+ (normal-mumble-remove-from-end)
+ (normal-mumble-remove))
+ (apply #'sb!sequence:remove item sequence args))))
+
+(define-sequence-traverser remove-if
+ (predicate sequence &rest args &key from-end start end count key)
#!+sb-doc
- "Returns a copy of sequence with elements such that predicate(element)
- is non-null are removed"
+ "Return a copy of sequence with elements satisfying PREDICATE removed."
(declare (fixnum start))
- (let* ((length (length sequence))
- (end (or end length))
- (count (or count most-positive-fixnum)))
- (declare (type index length end)
- (fixnum count))
+ (declare (truly-dynamic-extent args))
+ (let ((end (or end length)))
+ (declare (type index end))
(seq-dispatch sequence
- (if from-end
- (if-list-remove-from-end)
- (if-list-remove))
- (if from-end
- (if-mumble-remove-from-end)
- (if-mumble-remove)))))
-
-(defun remove-if-not (predicate sequence &key from-end (start 0) end count key)
+ (if from-end
+ (if-list-remove-from-end)
+ (if-list-remove))
+ (if from-end
+ (if-mumble-remove-from-end)
+ (if-mumble-remove))
+ (apply #'sb!sequence:remove-if predicate sequence args))))
+
+(define-sequence-traverser remove-if-not
+ (predicate sequence &rest args &key from-end start end count key)
#!+sb-doc
- "Returns a copy of sequence with elements such that predicate(element)
- is null are removed"
+ "Return a copy of sequence with elements not satisfying PREDICATE removed."
(declare (fixnum start))
- (let* ((length (length sequence))
- (end (or end length))
- (count (or count most-positive-fixnum)))
- (declare (type index length end)
- (fixnum count))
+ (declare (truly-dynamic-extent args))
+ (let ((end (or end length)))
+ (declare (type index end))
(seq-dispatch sequence
- (if from-end
- (if-not-list-remove-from-end)
- (if-not-list-remove))
- (if from-end
- (if-not-mumble-remove-from-end)
- (if-not-mumble-remove)))))
+ (if from-end
+ (if-not-list-remove-from-end)
+ (if-not-list-remove))
+ (if from-end
+ (if-not-mumble-remove-from-end)
+ (if-not-mumble-remove))
+ (apply #'sb!sequence:remove-if-not predicate sequence args))))
\f
;;;; REMOVE-DUPLICATES
(defun list-remove-duplicates* (list test test-not start end key from-end)
(declare (fixnum start))
(let* ((result (list ())) ; Put a marker on the beginning to splice with.
- (splice result)
- (current list))
+ (splice result)
+ (current list)
+ (end (or end (length list)))
+ (hash (and (> (- end start) 20)
+ test
+ (not key)
+ (not test-not)
+ (or (eql test #'eql)
+ (eql test #'eq)
+ (eql test #'equal)
+ (eql test #'equalp))
+ (make-hash-table :test test :size (- end start)))))
(do ((index 0 (1+ index)))
- ((= index start))
+ ((= index start))
(declare (fixnum index))
(setq splice (cdr (rplacd splice (list (car current)))))
(setq current (cdr current)))
- (do ((index 0 (1+ index)))
- ((or (and end (= index (the fixnum end)))
- (atom current)))
- (declare (fixnum index))
- (if (or (and from-end
- (not (member (apply-key key (car current))
- (nthcdr (1+ start) result)
- :test test
- :test-not test-not
- :key key)))
- (and (not from-end)
- (not (do ((it (apply-key key (car current)))
- (l (cdr current) (cdr l))
- (i (1+ index) (1+ i)))
- ((or (atom l) (and end (= i (the fixnum end))))
- ())
- (declare (fixnum i))
- (if (if test-not
- (not (funcall test-not it (apply-key key (car l))))
- (funcall test it (apply-key key (car l))))
- (return t))))))
- (setq splice (cdr (rplacd splice (list (car current))))))
- (setq current (cdr current)))
+ (if hash
+ (do ((index start (1+ index)))
+ ((or (and end (= index (the fixnum end)))
+ (atom current)))
+ (declare (fixnum index))
+ ;; The hash table contains links from values that are
+ ;; already in result to the cons cell *preceding* theirs
+ ;; in the list. That is, for each value v in the list,
+ ;; v and (cadr (gethash v hash)) are equal under TEST.
+ (let ((prev (gethash (car current) hash)))
+ (cond
+ ((not prev)
+ (setf (gethash (car current) hash) splice)
+ (setq splice (cdr (rplacd splice (list (car current))))))
+ ((not from-end)
+ (let* ((old (cdr prev))
+ (next (cdr old)))
+ (if next
+ (let ((next-val (car next)))
+ ;; (assert (eq (gethash next-val hash) old))
+ (setf (cdr prev) next
+ (gethash next-val hash) prev
+ (gethash (car current) hash) splice
+ splice (cdr (rplacd splice (list (car current))))))
+ (setf (car old) (car current)))))))
+ (setq current (cdr current)))
+ (do ((index start (1+ index)))
+ ((or (and end (= index (the fixnum end)))
+ (atom current)))
+ (declare (fixnum index))
+ (if (or (and from-end
+ (not (if test-not
+ (member (apply-key key (car current))
+ (nthcdr (1+ start) result)
+ :test-not test-not
+ :key key)
+ (member (apply-key key (car current))
+ (nthcdr (1+ start) result)
+ :test test
+ :key key))))
+ (and (not from-end)
+ (not (do ((it (apply-key key (car current)))
+ (l (cdr current) (cdr l))
+ (i (1+ index) (1+ i)))
+ ((or (atom l) (and end (= i (the fixnum end))))
+ ())
+ (declare (fixnum i))
+ (if (if test-not
+ (not (funcall test-not
+ it
+ (apply-key key (car l))))
+ (funcall test it (apply-key key (car l))))
+ (return t))))))
+ (setq splice (cdr (rplacd splice (list (car current))))))
+ (setq current (cdr current))))
(do ()
- ((atom current))
+ ((atom current))
(setq splice (cdr (rplacd splice (list (car current)))))
(setq current (cdr current)))
(cdr result)))
(defun vector-remove-duplicates* (vector test test-not start end key from-end
- &optional (length (length vector)))
+ &optional (length (length vector)))
(declare (vector vector) (fixnum start length))
(when (null end) (setf end (length vector)))
- (let ((result (make-sequence-like vector length))
- (index 0)
- (jndex start))
+ (let ((result (%make-sequence-like vector length))
+ (index 0)
+ (jndex start))
(declare (fixnum index jndex))
(do ()
- ((= index start))
+ ((= index start))
(setf (aref result index) (aref vector index))
(setq index (1+ index)))
(do ((elt))
- ((= index end))
+ ((= index end))
(setq elt (aref vector index))
(unless (or (and from-end
- (position (apply-key key elt) result :start start
- :end jndex :test test :test-not test-not :key key))
- (and (not from-end)
- (position (apply-key key elt) vector :start (1+ index)
- :end end :test test :test-not test-not :key key)))
- (setf (aref result jndex) elt)
- (setq jndex (1+ jndex)))
+ (if test-not
+ (position (apply-key key elt) result
+ :start start :end jndex
+ :test-not test-not :key key)
+ (position (apply-key key elt) result
+ :start start :end jndex
+ :test test :key key)))
+ (and (not from-end)
+ (if test-not
+ (position (apply-key key elt) vector
+ :start (1+ index) :end end
+ :test-not test-not :key key)
+ (position (apply-key key elt) vector
+ :start (1+ index) :end end
+ :test test :key key))))
+ (setf (aref result jndex) elt)
+ (setq jndex (1+ jndex)))
(setq index (1+ index)))
(do ()
- ((= index length))
+ ((= index length))
(setf (aref result jndex) (aref vector index))
(setq index (1+ index))
(setq jndex (1+ jndex)))
- (shrink-vector result jndex)))
-
-(defun remove-duplicates (sequence &key
- (test #'eql)
- test-not
- (start 0)
- from-end
- end
- key)
+ (%shrink-vector result jndex)))
+
+(define-sequence-traverser remove-duplicates
+ (sequence &rest args &key test test-not start end from-end key)
#!+sb-doc
- "The elements of Sequence are compared pairwise, and if any two match,
+ "The elements of SEQUENCE are compared pairwise, and if any two match,
the one occurring earlier is discarded, unless FROM-END is true, in
which case the one later in the sequence is discarded. The resulting
sequence is returned.
- The :TEST-NOT argument is depreciated."
+ The :TEST-NOT argument is deprecated."
(declare (fixnum start))
+ (declare (truly-dynamic-extent args))
(seq-dispatch sequence
- (if sequence
- (list-remove-duplicates* sequence test test-not
- start end key from-end))
- (vector-remove-duplicates* sequence test test-not
- start end key from-end)))
+ (if sequence
+ (list-remove-duplicates* sequence test test-not
+ start end key from-end))
+ (vector-remove-duplicates* sequence test test-not start end key from-end)
+ (apply #'sb!sequence:remove-duplicates sequence args)))
\f
;;;; DELETE-DUPLICATES
(declare (fixnum start))
(let ((handle (cons nil list)))
(do ((current (nthcdr start list) (cdr current))
- (previous (nthcdr start handle))
- (index start (1+ index)))
- ((or (and end (= index (the fixnum end))) (null current))
- (cdr handle))
+ (previous (nthcdr start handle))
+ (index start (1+ index)))
+ ((or (and end (= index (the fixnum end))) (null current))
+ (cdr handle))
(declare (fixnum index))
(if (do ((x (if from-end
- (nthcdr (1+ start) handle)
- (cdr current))
- (cdr x))
- (i (1+ index) (1+ i)))
- ((or (null x)
- (and (not from-end) end (= i (the fixnum end)))
- (eq x current))
- nil)
- (declare (fixnum i))
- (if (if test-not
- (not (funcall test-not
- (apply-key key (car current))
- (apply-key key (car x))))
- (funcall test
- (apply-key key (car current))
- (apply-key key (car x))))
- (return t)))
- (rplacd previous (cdr current))
- (setq previous (cdr previous))))))
+ (nthcdr (1+ start) handle)
+ (cdr current))
+ (cdr x))
+ (i (1+ index) (1+ i)))
+ ((or (null x)
+ (and (not from-end) end (= i (the fixnum end)))
+ (eq x current))
+ nil)
+ (declare (fixnum i))
+ (if (if test-not
+ (not (funcall test-not
+ (apply-key key (car current))
+ (apply-key key (car x))))
+ (funcall test
+ (apply-key key (car current))
+ (apply-key key (car x))))
+ (return t)))
+ (rplacd previous (cdr current))
+ (setq previous (cdr previous))))))
(defun vector-delete-duplicates* (vector test test-not key from-end start end
- &optional (length (length vector)))
+ &optional (length (length vector)))
(declare (vector vector) (fixnum start length))
(when (null end) (setf end (length vector)))
(do ((index start (1+ index))
(jndex start))
((= index end)
- (do ((index index (1+ index)) ; copy the rest of the vector
- (jndex jndex (1+ jndex)))
- ((= index length)
- (shrink-vector vector jndex)
- vector)
- (setf (aref vector jndex) (aref vector index))))
+ (do ((index index (1+ index)) ; copy the rest of the vector
+ (jndex jndex (1+ jndex)))
+ ((= index length)
+ (shrink-vector vector jndex))
+ (setf (aref vector jndex) (aref vector index))))
(declare (fixnum index jndex))
(setf (aref vector jndex) (aref vector index))
- (unless (position (apply-key key (aref vector index)) vector :key key
- :start (if from-end start (1+ index)) :test test
- :end (if from-end jndex end) :test-not test-not)
+ (unless (if test-not
+ (position (apply-key key (aref vector index)) vector :key key
+ :start (if from-end start (1+ index))
+ :end (if from-end jndex end)
+ :test-not test-not)
+ (position (apply-key key (aref vector index)) vector :key key
+ :start (if from-end start (1+ index))
+ :end (if from-end jndex end)
+ :test test))
(setq jndex (1+ jndex)))))
-(defun delete-duplicates (sequence &key
- (test #'eql)
- test-not
- (start 0)
- from-end
- end
- key)
+(define-sequence-traverser delete-duplicates
+ (sequence &rest args &key test test-not start end from-end key)
#!+sb-doc
- "The elements of Sequence are examined, and if any two match, one is
+ "The elements of SEQUENCE are examined, and if any two match, one is
discarded. The resulting sequence, which may be formed by destroying the
given sequence, is returned.
- The :TEST-NOT argument is depreciated."
+ The :TEST-NOT argument is deprecated."
+ (declare (truly-dynamic-extent args))
(seq-dispatch sequence
(if sequence
- (list-delete-duplicates* sequence test test-not key from-end start end))
- (vector-delete-duplicates* sequence test test-not key from-end start end)))
+ (list-delete-duplicates* sequence test test-not
+ key from-end start end))
+ (vector-delete-duplicates* sequence test test-not key from-end start end)
+ (apply #'sb!sequence:delete-duplicates sequence args)))
\f
;;;; SUBSTITUTE
(defun list-substitute* (pred new list start end count key test test-not old)
(declare (fixnum start end count))
(let* ((result (list nil))
- elt
- (splice result)
- (list list)) ; Get a local list for a stepper.
+ elt
+ (splice result)
+ (list list)) ; Get a local list for a stepper.
(do ((index 0 (1+ index)))
- ((= index start))
+ ((= index start))
(declare (fixnum index))
(setq splice (cdr (rplacd splice (list (car list)))))
(setq list (cdr list)))
(do ((index start (1+ index)))
- ((or (= index end) (null list) (= count 0)))
+ ((or (= index end) (null list) (= count 0)))
(declare (fixnum index))
(setq elt (car list))
(setq splice
- (cdr (rplacd splice
- (list
- (cond
- ((case pred
- (normal
- (if test-not
- (not
- (funcall test-not old (apply-key key elt)))
- (funcall test old (apply-key key elt))))
- (if (funcall test (apply-key key elt)))
- (if-not (not (funcall test (apply-key key elt)))))
- (setq count (1- count))
- new)
- (t elt))))))
+ (cdr (rplacd splice
+ (list
+ (cond
+ ((case pred
+ (normal
+ (if test-not
+ (not
+ (funcall test-not old (apply-key key elt)))
+ (funcall test old (apply-key key elt))))
+ (if (funcall test (apply-key key elt)))
+ (if-not (not (funcall test (apply-key key elt)))))
+ (decf count)
+ new)
+ (t elt))))))
(setq list (cdr list)))
(do ()
- ((null list))
+ ((null list))
(setq splice (cdr (rplacd splice (list (car list)))))
(setq list (cdr list)))
(cdr result)))
;;; Replace old with new in sequence moving from left to right by incrementer
;;; on each pass through the loop. Called by all three substitute functions.
(defun vector-substitute* (pred new sequence incrementer left right length
- start end count key test test-not old)
+ start end count key test test-not old)
(declare (fixnum start count end incrementer right))
- (let ((result (make-sequence-like sequence length))
- (index left))
+ (let ((result (%make-sequence-like sequence length))
+ (index left))
(declare (fixnum index))
(do ()
- ((= index start))
+ ((= index start))
(setf (aref result index) (aref sequence index))
(setq index (+ index incrementer)))
(do ((elt))
- ((or (= index end) (= count 0)))
+ ((or (= index end) (= count 0)))
(setq elt (aref sequence index))
(setf (aref result index)
- (cond ((case pred
- (normal
- (if test-not
- (not (funcall test-not old (apply-key key elt)))
- (funcall test old (apply-key key elt))))
- (if (funcall test (apply-key key elt)))
- (if-not (not (funcall test (apply-key key elt)))))
- (setq count (1- count))
- new)
- (t elt)))
+ (cond ((case pred
+ (normal
+ (if test-not
+ (not (funcall test-not old (apply-key key elt)))
+ (funcall test old (apply-key key elt))))
+ (if (funcall test (apply-key key elt)))
+ (if-not (not (funcall test (apply-key key elt)))))
+ (setq count (1- count))
+ new)
+ (t elt)))
(setq index (+ index incrementer)))
(do ()
- ((= index right))
+ ((= index right))
(setf (aref result index) (aref sequence index))
(setq index (+ index incrementer)))
result))
(eval-when (:compile-toplevel :execute)
(sb!xc:defmacro subst-dispatch (pred)
- `(if (listp sequence)
- (if from-end
- (nreverse (list-substitute* ,pred
- new
- (reverse sequence)
- (- (the fixnum length)
- (the fixnum end))
- (- (the fixnum length)
- (the fixnum start))
- count key test test-not old))
- (list-substitute* ,pred
- new sequence start end count key test test-not
- old))
- (if from-end
- (vector-substitute* ,pred new sequence -1 (1- (the fixnum length))
- -1 length (1- (the fixnum end))
- (1- (the fixnum start))
- count key test test-not old)
- (vector-substitute* ,pred new sequence 1 0 length length
- start end count key test test-not old))))
-
+ `(seq-dispatch sequence
+ (if from-end
+ (nreverse (list-substitute* ,pred
+ new
+ (reverse sequence)
+ (- (the fixnum length)
+ (the fixnum end))
+ (- (the fixnum length)
+ (the fixnum start))
+ count key test test-not old))
+ (list-substitute* ,pred
+ new sequence start end count key test test-not
+ old))
+ (if from-end
+ (vector-substitute* ,pred new sequence -1 (1- (the fixnum length))
+ -1 length (1- (the fixnum end))
+ (1- (the fixnum start))
+ count key test test-not old)
+ (vector-substitute* ,pred new sequence 1 0 length length
+ start end count key test test-not old))
+ ;; FIXME: wow, this is an odd way to implement the dispatch. PRED
+ ;; here is (QUOTE [NORMAL|IF|IF-NOT]). Not only is this pretty
+ ;; pointless, but also LIST-SUBSTITUTE* and VECTOR-SUBSTITUTE*
+ ;; dispatch once per element on PRED's run-time identity.
+ ,(ecase (cadr pred)
+ ((normal) `(apply #'sb!sequence:substitute new old sequence args))
+ ((if) `(apply #'sb!sequence:substitute-if new predicate sequence args))
+ ((if-not) `(apply #'sb!sequence:substitute-if-not new predicate sequence args)))))
) ; EVAL-WHEN
-(defun substitute (new old sequence &key from-end (test #'eql) test-not
- (start 0) count end key)
+(define-sequence-traverser substitute
+ (new old sequence &rest args &key from-end test test-not
+ start count end key)
#!+sb-doc
- "Returns a sequence of the same kind as Sequence with the same elements
- except that all elements equal to Old are replaced with New. See manual
- for details."
+ "Return a sequence of the same kind as SEQUENCE with the same elements,
+ except that all elements equal to OLD are replaced with NEW."
(declare (fixnum start))
- (let* ((length (length sequence))
- (end (or end length))
- (count (or count most-positive-fixnum)))
- (declare (type index length end)
- (fixnum count))
+ (declare (truly-dynamic-extent args))
+ (let ((end (or end length)))
+ (declare (type index end))
(subst-dispatch 'normal)))
\f
;;;; SUBSTITUTE-IF, SUBSTITUTE-IF-NOT
-(defun substitute-if (new test sequence &key from-end (start 0) end count key)
+(define-sequence-traverser substitute-if
+ (new predicate sequence &rest args &key from-end start end count key)
#!+sb-doc
- "Returns a sequence of the same kind as Sequence with the same elements
- except that all elements satisfying the Test are replaced with New. See
- manual for details."
+ "Return a sequence of the same kind as SEQUENCE with the same elements
+ except that all elements satisfying the PRED are replaced with NEW."
+ (declare (truly-dynamic-extent args))
(declare (fixnum start))
- (let* ((length (length sequence))
- (end (or end length))
- (count (or count most-positive-fixnum))
- test-not
- old)
- (declare (type index length end)
- (fixnum count))
+ (let ((end (or end length))
+ (test predicate)
+ (test-not nil)
+ old)
+ (declare (type index length end))
(subst-dispatch 'if)))
-(defun substitute-if-not (new test sequence &key from-end (start 0)
- end count key)
+(define-sequence-traverser substitute-if-not
+ (new predicate sequence &rest args &key from-end start end count key)
#!+sb-doc
- "Returns a sequence of the same kind as Sequence with the same elements
- except that all elements not satisfying the Test are replaced with New.
- See manual for details."
+ "Return a sequence of the same kind as SEQUENCE with the same elements
+ except that all elements not satisfying the PRED are replaced with NEW."
+ (declare (truly-dynamic-extent args))
(declare (fixnum start))
- (let* ((length (length sequence))
- (end (or end length))
- (count (or count most-positive-fixnum))
- test-not
- old)
- (declare (type index length end)
- (fixnum count))
+ (let ((end (or end length))
+ (test predicate)
+ (test-not nil)
+ old)
+ (declare (type index length end))
(subst-dispatch 'if-not)))
\f
;;;; NSUBSTITUTE
-(defun nsubstitute (new old sequence &key from-end (test #'eql) test-not
- end count key (start 0))
+(define-sequence-traverser nsubstitute
+ (new old sequence &rest args &key from-end test test-not
+ end count key start)
#!+sb-doc
- "Returns a sequence of the same kind as Sequence with the same elements
- except that all elements equal to Old are replaced with New. The Sequence
- may be destroyed. See manual for details."
+ "Return a sequence of the same kind as SEQUENCE with the same elements
+ except that all elements equal to OLD are replaced with NEW. SEQUENCE
+ may be destructively modified."
(declare (fixnum start))
- (let ((end (or end (length sequence)))
- (count (or count most-positive-fixnum)))
- (declare (fixnum count))
- (if (listp sequence)
- (if from-end
- (nreverse (nlist-substitute*
- new old (nreverse (the list sequence))
- test test-not start end count key))
- (nlist-substitute* new old sequence
- test test-not start end count key))
- (if from-end
- (nvector-substitute* new old sequence -1
- test test-not (1- end) (1- start) count key)
- (nvector-substitute* new old sequence 1
- test test-not start end count key)))))
+ (declare (truly-dynamic-extent args))
+ (let ((end (or end length)))
+ (seq-dispatch sequence
+ (if from-end
+ (let ((length (length sequence)))
+ (nreverse (nlist-substitute*
+ new old (nreverse (the list sequence))
+ test test-not (- length end) (- length start)
+ count key)))
+ (nlist-substitute* new old sequence
+ test test-not start end count key))
+ (if from-end
+ (nvector-substitute* new old sequence -1
+ test test-not (1- end) (1- start) count key)
+ (nvector-substitute* new old sequence 1
+ test test-not start end count key))
+ (apply #'sb!sequence:nsubstitute new old sequence args))))
(defun nlist-substitute* (new old sequence test test-not start end count key)
(declare (fixnum start count end))
((or (= index end) (null list) (= count 0)) sequence)
(declare (fixnum index))
(when (if test-not
- (not (funcall test-not old (apply-key key (car list))))
- (funcall test old (apply-key key (car list))))
+ (not (funcall test-not old (apply-key key (car list))))
+ (funcall test old (apply-key key (car list))))
(rplaca list new)
(setq count (1- count)))))
(defun nvector-substitute* (new old sequence incrementer
- test test-not start end count key)
+ test test-not start end count key)
(declare (fixnum start incrementer count end))
(do ((index start (+ index incrementer)))
((or (= index end) (= count 0)) sequence)
(declare (fixnum index))
(when (if test-not
- (not (funcall test-not
- old
- (apply-key key (aref sequence index))))
- (funcall test old (apply-key key (aref sequence index))))
+ (not (funcall test-not
+ old
+ (apply-key key (aref sequence index))))
+ (funcall test old (apply-key key (aref sequence index))))
(setf (aref sequence index) new)
(setq count (1- count)))))
\f
;;;; NSUBSTITUTE-IF, NSUBSTITUTE-IF-NOT
-(defun nsubstitute-if (new test sequence &key from-end (start 0) end count key)
+(define-sequence-traverser nsubstitute-if
+ (new predicate sequence &rest args &key from-end start end count key)
#!+sb-doc
- "Returns a sequence of the same kind as Sequence with the same elements
- except that all elements satisfying the Test are replaced with New. The
- Sequence may be destroyed. See manual for details."
+ "Return a sequence of the same kind as SEQUENCE with the same elements
+ except that all elements satisfying PREDICATE are replaced with NEW.
+ SEQUENCE may be destructively modified."
(declare (fixnum start))
- (let ((end (or end (length sequence)))
- (count (or count most-positive-fixnum)))
- (declare (fixnum end count))
- (if (listp sequence)
- (if from-end
- (nreverse (nlist-substitute-if*
- new test (nreverse (the list sequence))
- start end count key))
- (nlist-substitute-if* new test sequence
- start end count key))
- (if from-end
- (nvector-substitute-if* new test sequence -1
- (1- end) (1- start) count key)
- (nvector-substitute-if* new test sequence 1
- start end count key)))))
+ (declare (truly-dynamic-extent args))
+ (let ((end (or end length)))
+ (declare (fixnum end))
+ (seq-dispatch sequence
+ (if from-end
+ (let ((length (length sequence)))
+ (nreverse (nlist-substitute-if*
+ new predicate (nreverse (the list sequence))
+ (- length end) (- length start) count key)))
+ (nlist-substitute-if* new predicate sequence
+ start end count key))
+ (if from-end
+ (nvector-substitute-if* new predicate sequence -1
+ (1- end) (1- start) count key)
+ (nvector-substitute-if* new predicate sequence 1
+ start end count key))
+ (apply #'sb!sequence:nsubstitute-if new predicate sequence args))))
(defun nlist-substitute-if* (new test sequence start end count key)
(declare (fixnum end))
(setq count (1- count)))))
(defun nvector-substitute-if* (new test sequence incrementer
- start end count key)
+ start end count key)
(do ((index start (+ index incrementer)))
((or (= index end) (= count 0)) sequence)
(when (funcall test (apply-key key (aref sequence index)))
(setf (aref sequence index) new)
(setq count (1- count)))))
-(defun nsubstitute-if-not (new test sequence &key from-end (start 0)
- end count key)
+(define-sequence-traverser nsubstitute-if-not
+ (new predicate sequence &rest args &key from-end start end count key)
#!+sb-doc
- "Returns a sequence of the same kind as Sequence with the same elements
- except that all elements not satisfying the Test are replaced with New.
- The Sequence may be destroyed. See manual for details."
+ "Return a sequence of the same kind as SEQUENCE with the same elements
+ except that all elements not satisfying PREDICATE are replaced with NEW.
+ SEQUENCE may be destructively modified."
(declare (fixnum start))
- (let ((end (or end (length sequence)))
- (count (or count most-positive-fixnum)))
- (declare (fixnum end count))
- (if (listp sequence)
- (if from-end
- (nreverse (nlist-substitute-if-not*
- new test (nreverse (the list sequence))
- start end count key))
- (nlist-substitute-if-not* new test sequence
- start end count key))
- (if from-end
- (nvector-substitute-if-not* new test sequence -1
- (1- end) (1- start) count key)
- (nvector-substitute-if-not* new test sequence 1
- start end count key)))))
+ (declare (truly-dynamic-extent args))
+ (let ((end (or end length)))
+ (declare (fixnum end))
+ (seq-dispatch sequence
+ (if from-end
+ (let ((length (length sequence)))
+ (nreverse (nlist-substitute-if-not*
+ new predicate (nreverse (the list sequence))
+ (- length end) (- length start) count key)))
+ (nlist-substitute-if-not* new predicate sequence
+ start end count key))
+ (if from-end
+ (nvector-substitute-if-not* new predicate sequence -1
+ (1- end) (1- start) count key)
+ (nvector-substitute-if-not* new predicate sequence 1
+ start end count key))
+ (apply #'sb!sequence:nsubstitute-if-not new predicate sequence args))))
(defun nlist-substitute-if-not* (new test sequence start end count key)
(declare (fixnum end))
((or (= index end) (null list) (= count 0)) sequence)
(when (not (funcall test (apply-key key (car list))))
(rplaca list new)
- (setq count (1- count)))))
+ (decf count))))
(defun nvector-substitute-if-not* (new test sequence incrementer
- start end count key)
+ start end count key)
(do ((index start (+ index incrementer)))
((or (= index end) (= count 0)) sequence)
(when (not (funcall test (apply-key key (aref sequence index))))
(setf (aref sequence index) new)
- (setq count (1- count)))))
-\f
-;;; locater macros used by FIND and POSITION
-
-(eval-when (:compile-toplevel :execute)
-
-(sb!xc:defmacro vector-locater-macro (sequence body-form return-type)
- `(let ((incrementer (if from-end -1 1))
- (start (if from-end (1- (the fixnum end)) start))
- (end (if from-end (1- (the fixnum start)) end)))
- (declare (fixnum start end incrementer))
- (do ((index start (+ index incrementer))
- ,@(case return-type (:position nil) (:element '(current))))
- ((= index end) ())
- (declare (fixnum index))
- ,@(case return-type
- (:position nil)
- (:element `((setf current (aref ,sequence index)))))
- ,body-form)))
-
-(sb!xc:defmacro locater-test-not (item sequence seq-type return-type)
- (let ((seq-ref (case return-type
- (:position
- (case seq-type
- (:vector `(aref ,sequence index))
- (:list `(pop ,sequence))))
- (:element 'current)))
- (return (case return-type
- (:position 'index)
- (:element 'current))))
- `(if test-not
- (if (not (funcall test-not ,item (apply-key key ,seq-ref)))
- (return ,return))
- (if (funcall test ,item (apply-key key ,seq-ref))
- (return ,return)))))
-
-(sb!xc:defmacro vector-locater (item sequence return-type)
- `(vector-locater-macro ,sequence
- (locater-test-not ,item ,sequence :vector ,return-type)
- ,return-type))
+ (decf count))))
\f
-(sb!xc:defmacro locater-if-test (test sequence seq-type return-type sense)
- (let ((seq-ref (case return-type
- (:position
- (case seq-type
- (:vector `(aref ,sequence index))
- (:list `(pop ,sequence))))
- (:element 'current)))
- (return (case return-type
- (:position 'index)
- (:element 'current))))
- (if sense
- `(if (funcall ,test (apply-key key ,seq-ref))
- (return ,return))
- `(if (not (funcall ,test (apply-key key ,seq-ref)))
- (return ,return)))))
-
-(sb!xc:defmacro vector-locater-if-macro (test sequence return-type sense)
- `(vector-locater-macro ,sequence
- (locater-if-test ,test ,sequence :vector ,return-type ,sense)
- ,return-type))
-
-(sb!xc:defmacro vector-locater-if (test sequence return-type)
- `(vector-locater-if-macro ,test ,sequence ,return-type t))
-
-(sb!xc:defmacro vector-locater-if-not (test sequence return-type)
- `(vector-locater-if-macro ,test ,sequence ,return-type nil))
-\f
-(sb!xc:defmacro list-locater-macro (sequence body-form return-type)
- `(if from-end
- (do ((sequence (nthcdr (- (the fixnum (length sequence))
- (the fixnum end))
- (reverse (the list ,sequence))))
- (index (1- (the fixnum end)) (1- index))
- (terminus (1- (the fixnum start)))
- ,@(case return-type (:position nil) (:element '(current))))
- ((or (= index terminus) (null sequence)) ())
- (declare (fixnum index terminus))
- ,@(case return-type
- (:position nil)
- (:element `((setf current (pop ,sequence)))))
- ,body-form)
- (do ((sequence (nthcdr start ,sequence))
- (index start (1+ index))
- ,@(case return-type (:position nil) (:element '(current))))
- ((or (= index (the fixnum end)) (null sequence)) ())
- (declare (fixnum index))
- ,@(case return-type
- (:position nil)
- (:element `((setf current (pop ,sequence)))))
- ,body-form)))
-
-(sb!xc:defmacro list-locater (item sequence return-type)
- `(list-locater-macro ,sequence
- (locater-test-not ,item ,sequence :list ,return-type)
- ,return-type))
-
-(sb!xc:defmacro list-locater-if-macro (test sequence return-type sense)
- `(list-locater-macro ,sequence
- (locater-if-test ,test ,sequence :list ,return-type ,sense)
- ,return-type))
-
-(sb!xc:defmacro list-locater-if (test sequence return-type)
- `(list-locater-if-macro ,test ,sequence ,return-type t))
-
-(sb!xc:defmacro list-locater-if-not (test sequence return-type)
- `(list-locater-if-macro ,test ,sequence ,return-type nil))
-
-) ; EVAL-WHEN
-\f
-;;; POSITION
-
-(eval-when (:compile-toplevel :execute)
-
-(sb!xc:defmacro vector-position (item sequence)
- `(vector-locater ,item ,sequence :position))
-
-(sb!xc:defmacro list-position (item sequence)
- `(list-locater ,item ,sequence :position))
-
-) ; EVAL-WHEN
-
-;;; POSITION cannot default end to the length of sequence since it is not
-;;; an error to supply nil for its value. We must test for end being nil
-;;; in the body of the function, and this is actually done in the support
-;;; routines for other reasons (see below).
-(defun position (item sequence &key from-end (test #'eql) test-not (start 0)
- end key)
- #!+sb-doc
- "Returns the zero-origin index of the first element in SEQUENCE
- satisfying the test (default is EQL) with the given ITEM"
+;;;; FIND, POSITION, and their -IF and -IF-NOT variants
+
+(defun effective-find-position-test (test test-not)
+ (effective-find-position-test test test-not))
+(defun effective-find-position-key (key)
+ (effective-find-position-key key))
+
+;;; shared guts of out-of-line FIND, POSITION, FIND-IF, and POSITION-IF
+(macrolet (;; shared logic for defining %FIND-POSITION and
+ ;; %FIND-POSITION-IF in terms of various inlineable cases
+ ;; of the expression defined in FROB and VECTOR*-FROB
+ (frobs ()
+ `(seq-dispatch sequence-arg
+ (frob sequence-arg from-end)
+ (with-array-data ((sequence sequence-arg :offset-var offset)
+ (start start)
+ (end end)
+ :check-fill-pointer t)
+ (multiple-value-bind (f p)
+ (macrolet ((frob2 () '(if from-end
+ (frob sequence t)
+ (frob sequence nil))))
+ (typecase sequence
+ #!+sb-unicode
+ ((simple-array character (*)) (frob2))
+ ((simple-array base-char (*)) (frob2))
+ (t (vector*-frob sequence))))
+ (declare (type (or index null) p))
+ (values f (and p (the index (- p offset)))))))))
+ (defun %find-position (item sequence-arg from-end start end key test)
+ (macrolet ((frob (sequence from-end)
+ `(%find-position item ,sequence
+ ,from-end start end key test))
+ (vector*-frob (sequence)
+ `(%find-position-vector-macro item ,sequence
+ from-end start end key test)))
+ (frobs)))
+ (defun %find-position-if (predicate sequence-arg from-end start end key)
+ (macrolet ((frob (sequence from-end)
+ `(%find-position-if predicate ,sequence
+ ,from-end start end key))
+ (vector*-frob (sequence)
+ `(%find-position-if-vector-macro predicate ,sequence
+ from-end start end key)))
+ (frobs)))
+ (defun %find-position-if-not (predicate sequence-arg from-end start end key)
+ (macrolet ((frob (sequence from-end)
+ `(%find-position-if-not predicate ,sequence
+ ,from-end start end key))
+ (vector*-frob (sequence)
+ `(%find-position-if-not-vector-macro predicate ,sequence
+ from-end start end key)))
+ (frobs))))
+
+(defun find
+ (item sequence &rest args &key from-end (start 0) end key test test-not)
+ (declare (truly-dynamic-extent args))
(seq-dispatch sequence
- (list-position* item sequence from-end test test-not start end key)
- (vector-position* item sequence from-end test test-not start end key)))
-
-;;; The support routines for SUBSEQ are used by compiler transforms, so we
-;;; worry about dealing with END being supplied or defaulting to NIL
-;;; at this level.
-
-(defun list-position* (item sequence from-end test test-not start end key)
- (declare (fixnum start))
- (when (null end) (setf end (length sequence)))
- (list-position item sequence))
-
-(defun vector-position* (item sequence from-end test test-not start end key)
- (declare (fixnum start))
- (when (null end) (setf end (length sequence)))
- (vector-position item sequence))
-\f
-;;;; POSITION-IF
-
-(eval-when (:compile-toplevel :execute)
-
-(sb!xc:defmacro vector-position-if (test sequence)
- `(vector-locater-if ,test ,sequence :position))
-
-(sb!xc:defmacro list-position-if (test sequence)
- `(list-locater-if ,test ,sequence :position))
-
-) ; EVAL-WHEN
-
-(defun position-if (test sequence &key from-end (start 0) key end)
- #!+sb-doc
- "Returns the zero-origin index of the first element satisfying test(el)"
- (declare (fixnum start))
- (let ((end (or end (length sequence))))
- (declare (type index end))
- (seq-dispatch sequence
- (list-position-if test sequence)
- (vector-position-if test sequence))))
-\f
-;;;; POSITION-IF-NOT
-
-(eval-when (:compile-toplevel :execute)
-
-(sb!xc:defmacro vector-position-if-not (test sequence)
- `(vector-locater-if-not ,test ,sequence :position))
-
-(sb!xc:defmacro list-position-if-not (test sequence)
- `(list-locater-if-not ,test ,sequence :position))
-
-) ; EVAL-WHEN
-
-(defun position-if-not (test sequence &key from-end (start 0) key end)
- #!+sb-doc
- "Returns the zero-origin index of the first element not satisfying test(el)"
- (declare (fixnum start))
- (let ((end (or end (length sequence))))
- (declare (type index end))
- (seq-dispatch sequence
- (list-position-if-not test sequence)
- (vector-position-if-not test sequence))))
-\f
-;;;; FIND
-
-(eval-when (:compile-toplevel :execute)
-
-(sb!xc:defmacro vector-find (item sequence)
- `(vector-locater ,item ,sequence :element))
-
-(sb!xc:defmacro list-find (item sequence)
- `(list-locater ,item ,sequence :element))
-
-) ; EVAL-WHEN
-
-;;; Note: FIND cannot default end to the length of sequence since it
-;;; is not an error to supply NIL for its value. We must test for end
-;;; being NIL in the body of the function, and this is actually done
-;;; in the support routines for other reasons (see above).
-(defun find (item sequence &key from-end (test #'eql) test-not (start 0)
- end key)
- #!+sb-doc
- "Returns the first element in SEQUENCE satisfying the test (default
- is EQL) with the given ITEM"
- (declare (fixnum start))
+ (nth-value 0 (%find-position
+ item sequence from-end start end
+ (effective-find-position-key key)
+ (effective-find-position-test test test-not)))
+ (nth-value 0 (%find-position
+ item sequence from-end start end
+ (effective-find-position-key key)
+ (effective-find-position-test test test-not)))
+ (apply #'sb!sequence:find item sequence args)))
+(defun position
+ (item sequence &rest args &key from-end (start 0) end key test test-not)
+ (declare (truly-dynamic-extent args))
(seq-dispatch sequence
- (list-find* item sequence from-end test test-not start end key)
- (vector-find* item sequence from-end test test-not start end key)))
-
-;;; The support routines for FIND are used by compiler transforms, so we
-;;; worry about dealing with END being supplied or defaulting to NIL
-;;; at this level.
-
-(defun list-find* (item sequence from-end test test-not start end key)
- (when (null end) (setf end (length sequence)))
- (list-find item sequence))
-
-(defun vector-find* (item sequence from-end test test-not start end key)
- (when (null end) (setf end (length sequence)))
- (vector-find item sequence))
+ (nth-value 1 (%find-position
+ item sequence from-end start end
+ (effective-find-position-key key)
+ (effective-find-position-test test test-not)))
+ (nth-value 1 (%find-position
+ item sequence from-end start end
+ (effective-find-position-key key)
+ (effective-find-position-test test test-not)))
+ (apply #'sb!sequence:position item sequence args)))
+
+(defun find-if (predicate sequence &rest args &key from-end (start 0) end key)
+ (declare (truly-dynamic-extent args))
+ (seq-dispatch sequence
+ (nth-value 0 (%find-position-if
+ (%coerce-callable-to-fun predicate)
+ sequence from-end start end
+ (effective-find-position-key key)))
+ (nth-value 0 (%find-position-if
+ (%coerce-callable-to-fun predicate)
+ sequence from-end start end
+ (effective-find-position-key key)))
+ (apply #'sb!sequence:find-if predicate sequence args)))
+(defun position-if
+ (predicate sequence &rest args &key from-end (start 0) end key)
+ (declare (truly-dynamic-extent args))
+ (seq-dispatch sequence
+ (nth-value 1 (%find-position-if
+ (%coerce-callable-to-fun predicate)
+ sequence from-end start end
+ (effective-find-position-key key)))
+ (nth-value 1 (%find-position-if
+ (%coerce-callable-to-fun predicate)
+ sequence from-end start end
+ (effective-find-position-key key)))
+ (apply #'sb!sequence:position-if predicate sequence args)))
+
+(defun find-if-not
+ (predicate sequence &rest args &key from-end (start 0) end key)
+ (declare (truly-dynamic-extent args))
+ (seq-dispatch sequence
+ (nth-value 0 (%find-position-if-not
+ (%coerce-callable-to-fun predicate)
+ sequence from-end start end
+ (effective-find-position-key key)))
+ (nth-value 0 (%find-position-if-not
+ (%coerce-callable-to-fun predicate)
+ sequence from-end start end
+ (effective-find-position-key key)))
+ (apply #'sb!sequence:find-if-not predicate sequence args)))
+(defun position-if-not
+ (predicate sequence &rest args &key from-end (start 0) end key)
+ (declare (truly-dynamic-extent args))
+ (seq-dispatch sequence
+ (nth-value 1 (%find-position-if-not
+ (%coerce-callable-to-fun predicate)
+ sequence from-end start end
+ (effective-find-position-key key)))
+ (nth-value 1 (%find-position-if-not
+ (%coerce-callable-to-fun predicate)
+ sequence from-end start end
+ (effective-find-position-key key)))
+ (apply #'sb!sequence:position-if-not predicate sequence args)))
\f
-;;;; FIND-IF and FIND-IF-NOT
+;;;; COUNT-IF, COUNT-IF-NOT, and COUNT
(eval-when (:compile-toplevel :execute)
-(sb!xc:defmacro vector-find-if (test sequence)
- `(vector-locater-if ,test ,sequence :element))
+(sb!xc:defmacro vector-count-if (notp from-end-p predicate sequence)
+ (let ((next-index (if from-end-p '(1- index) '(1+ index)))
+ (pred `(funcall ,predicate (apply-key key (aref ,sequence index)))))
+ `(let ((%start ,(if from-end-p '(1- end) 'start))
+ (%end ,(if from-end-p '(1- start) 'end)))
+ (do ((index %start ,next-index)
+ (count 0))
+ ((= index (the fixnum %end)) count)
+ (declare (fixnum index count))
+ (,(if notp 'unless 'when) ,pred
+ (setq count (1+ count)))))))
+
+(sb!xc:defmacro list-count-if (notp from-end-p predicate sequence)
+ (let ((pred `(funcall ,predicate (apply-key key (pop sequence)))))
+ `(let ((%start ,(if from-end-p '(- length end) 'start))
+ (%end ,(if from-end-p '(- length start) 'end))
+ (sequence ,(if from-end-p '(reverse sequence) 'sequence)))
+ (do ((sequence (nthcdr %start ,sequence))
+ (index %start (1+ index))
+ (count 0))
+ ((or (= index (the fixnum %end)) (null sequence)) count)
+ (declare (fixnum index count))
+ (,(if notp 'unless 'when) ,pred
+ (setq count (1+ count)))))))
-(sb!xc:defmacro list-find-if (test sequence)
- `(list-locater-if ,test ,sequence :element))
) ; EVAL-WHEN
-(defun find-if (test sequence &key from-end (start 0) end key)
+(define-sequence-traverser count-if
+ (pred sequence &rest args &key from-end start end key)
#!+sb-doc
- "Returns the zero-origin index of the first element satisfying the test."
+ "Return the number of elements in SEQUENCE satisfying PRED(el)."
(declare (fixnum start))
- (let ((end (or end (length sequence))))
+ (declare (truly-dynamic-extent args))
+ (let ((end (or end length))
+ (pred (%coerce-callable-to-fun pred)))
(declare (type index end))
(seq-dispatch sequence
- (list-find-if test sequence)
- (vector-find-if test sequence))))
-
-(eval-when (:compile-toplevel :execute)
-
-(sb!xc:defmacro vector-find-if-not (test sequence)
- `(vector-locater-if-not ,test ,sequence :element))
-
-(sb!xc:defmacro list-find-if-not (test sequence)
- `(list-locater-if-not ,test ,sequence :element))
-
-) ; EVAL-WHEN
+ (if from-end
+ (list-count-if nil t pred sequence)
+ (list-count-if nil nil pred sequence))
+ (if from-end
+ (vector-count-if nil t pred sequence)
+ (vector-count-if nil nil pred sequence))
+ (apply #'sb!sequence:count-if pred sequence args))))
-(defun find-if-not (test sequence &key from-end (start 0) end key)
+(define-sequence-traverser count-if-not
+ (pred sequence &rest args &key from-end start end key)
#!+sb-doc
- "Returns the zero-origin index of the first element not satisfying the test."
+ "Return the number of elements in SEQUENCE not satisfying TEST(el)."
(declare (fixnum start))
- (let ((end (or end (length sequence))))
+ (declare (truly-dynamic-extent args))
+ (let ((end (or end length))
+ (pred (%coerce-callable-to-fun pred)))
(declare (type index end))
(seq-dispatch sequence
- (list-find-if-not test sequence)
- (vector-find-if-not test sequence))))
-\f
-;;;; COUNT
-
-(eval-when (:compile-toplevel :execute)
-
-(sb!xc:defmacro vector-count (item sequence)
- `(do ((index start (1+ index))
- (count 0))
- ((= index (the fixnum end)) count)
- (declare (fixnum index count))
- (if test-not
- (unless (funcall test-not ,item
- (apply-key key (aref ,sequence index)))
- (setq count (1+ count)))
- (when (funcall test ,item (apply-key key (aref ,sequence index)))
- (setq count (1+ count))))))
-
-(sb!xc:defmacro list-count (item sequence)
- `(do ((sequence (nthcdr start ,sequence))
- (index start (1+ index))
- (count 0))
- ((or (= index (the fixnum end)) (null sequence)) count)
- (declare (fixnum index count))
- (if test-not
- (unless (funcall test-not ,item (apply-key key (pop sequence)))
- (setq count (1+ count)))
- (when (funcall test ,item (apply-key key (pop sequence)))
- (setq count (1+ count))))))
-
-) ; EVAL-WHEN
+ (if from-end
+ (list-count-if t t pred sequence)
+ (list-count-if t nil pred sequence))
+ (if from-end
+ (vector-count-if t t pred sequence)
+ (vector-count-if t nil pred sequence))
+ (apply #'sb!sequence:count-if-not pred sequence args))))
-(defun count (item sequence &key from-end (test #'eql) test-not (start 0)
- end key)
+(define-sequence-traverser count
+ (item sequence &rest args &key from-end start end
+ key (test #'eql test-p) (test-not nil test-not-p))
#!+sb-doc
- "Returns the number of elements in SEQUENCE satisfying a test with ITEM,
+ "Return the number of elements in SEQUENCE satisfying a test with ITEM,
which defaults to EQL."
- (declare (ignore from-end) (fixnum start))
- (let ((end (or end (length sequence))))
- (declare (type index end))
- (seq-dispatch sequence
- (list-count item sequence)
- (vector-count item sequence))))
-\f
-;;;; COUNT-IF and COUNT-IF-NOT
-
-(eval-when (:compile-toplevel :execute)
-
-(sb!xc:defmacro vector-count-if (predicate sequence)
- `(do ((index start (1+ index))
- (count 0))
- ((= index (the fixnum end)) count)
- (declare (fixnum index count))
- (if (funcall ,predicate (apply-key key (aref ,sequence index)))
- (setq count (1+ count)))))
-
-(sb!xc:defmacro list-count-if (predicate sequence)
- `(do ((sequence (nthcdr start ,sequence))
- (index start (1+ index))
- (count 0))
- ((or (= index (the fixnum end)) (null sequence)) count)
- (declare (fixnum index count))
- (if (funcall ,predicate (apply-key key (pop sequence)))
- (setq count (1+ count)))))
-
-) ; EVAL-WHEN
-
-(defun count-if (test sequence &key from-end (start 0) end key)
- #!+sb-doc
- "Returns the number of elements in SEQUENCE satisfying TEST(el)."
- (declare (ignore from-end) (fixnum start))
- (let ((end (or end (length sequence))))
- (declare (type index end))
- (seq-dispatch sequence
- (list-count-if test sequence)
- (vector-count-if test sequence))))
-
-(eval-when (:compile-toplevel :execute)
-
-(sb!xc:defmacro vector-count-if-not (predicate sequence)
- `(do ((index start (1+ index))
- (count 0))
- ((= index (the fixnum end)) count)
- (declare (fixnum index count))
- (if (not (funcall ,predicate (apply-key key (aref ,sequence index))))
- (setq count (1+ count)))))
-
-(sb!xc:defmacro list-count-if-not (predicate sequence)
- `(do ((sequence (nthcdr start ,sequence))
- (index start (1+ index))
- (count 0))
- ((or (= index (the fixnum end)) (null sequence)) count)
- (declare (fixnum index count))
- (if (not (funcall ,predicate (apply-key key (pop sequence))))
- (setq count (1+ count)))))
-
-) ; EVAL-WHEN
-
-(defun count-if-not (test sequence &key from-end (start 0) end key)
- #!+sb-doc
- "Returns the number of elements in SEQUENCE not satisfying TEST(el)."
- (declare (ignore from-end) (fixnum start))
- (let ((end (or end (length sequence))))
+ (declare (fixnum start))
+ (declare (truly-dynamic-extent args))
+ (when (and test-p test-not-p)
+ ;; ANSI Common Lisp has left the behavior in this situation unspecified.
+ ;; (CLHS 17.2.1)
+ (error ":TEST and :TEST-NOT are both present."))
+ (let ((end (or end length)))
(declare (type index end))
- (seq-dispatch sequence
- (list-count-if-not test sequence)
- (vector-count-if-not test sequence))))
+ (let ((%test (if test-not-p
+ (lambda (x)
+ (not (funcall test-not item x)))
+ (lambda (x)
+ (funcall test item x)))))
+ (seq-dispatch sequence
+ (if from-end
+ (list-count-if nil t %test sequence)
+ (list-count-if nil nil %test sequence))
+ (if from-end
+ (vector-count-if nil t %test sequence)
+ (vector-count-if nil nil %test sequence))
+ (apply #'sb!sequence:count item sequence args)))))
\f
;;;; MISMATCH
(sb!xc:defmacro match-vars (&rest body)
`(let ((inc (if from-end -1 1))
- (start1 (if from-end (1- (the fixnum end1)) start1))
- (start2 (if from-end (1- (the fixnum end2)) start2))
- (end1 (if from-end (1- (the fixnum start1)) end1))
- (end2 (if from-end (1- (the fixnum start2)) end2)))
+ (start1 (if from-end (1- (the fixnum end1)) start1))
+ (start2 (if from-end (1- (the fixnum end2)) start2))
+ (end1 (if from-end (1- (the fixnum start1)) end1))
+ (end2 (if from-end (1- (the fixnum start2)) end2)))
(declare (fixnum inc start1 start2 end1 end2))
,@body))
(sb!xc:defmacro matchify-list ((sequence start length end) &body body)
(declare (ignore end)) ;; ### Should END be used below?
`(let ((,sequence (if from-end
- (nthcdr (- (the fixnum ,length) (the fixnum ,start) 1)
- (reverse (the list ,sequence)))
- (nthcdr ,start ,sequence))))
+ (nthcdr (- (the fixnum ,length) (the fixnum ,start) 1)
+ (reverse (the list ,sequence)))
+ (nthcdr ,start ,sequence))))
(declare (type list ,sequence))
,@body))
(sb!xc:defmacro if-mismatch (elt1 elt2)
`(cond ((= (the fixnum index1) (the fixnum end1))
- (return (if (= (the fixnum index2) (the fixnum end2))
- nil
- (if from-end
- (1+ (the fixnum index1))
- (the fixnum index1)))))
- ((= (the fixnum index2) (the fixnum end2))
- (return (if from-end (1+ (the fixnum index1)) index1)))
- (test-not
- (if (funcall test-not (apply-key key ,elt1) (apply-key key ,elt2))
- (return (if from-end (1+ (the fixnum index1)) index1))))
- (t (if (not (funcall test (apply-key key ,elt1)
- (apply-key key ,elt2)))
- (return (if from-end (1+ (the fixnum index1)) index1))))))
+ (return (if (= (the fixnum index2) (the fixnum end2))
+ nil
+ (if from-end
+ (1+ (the fixnum index1))
+ (the fixnum index1)))))
+ ((= (the fixnum index2) (the fixnum end2))
+ (return (if from-end (1+ (the fixnum index1)) index1)))
+ (test-not
+ (if (funcall test-not (apply-key key ,elt1) (apply-key key ,elt2))
+ (return (if from-end (1+ (the fixnum index1)) index1))))
+ (t (if (not (funcall test (apply-key key ,elt1)
+ (apply-key key ,elt2)))
+ (return (if from-end (1+ (the fixnum index1)) index1))))))
(sb!xc:defmacro mumble-mumble-mismatch ()
`(do ((index1 start1 (+ index1 (the fixnum inc)))
- (index2 start2 (+ index2 (the fixnum inc))))
+ (index2 start2 (+ index2 (the fixnum inc))))
(())
(declare (fixnum index1 index2))
(if-mismatch (aref sequence1 index1) (aref sequence2 index2))))
(sb!xc:defmacro mumble-list-mismatch ()
`(do ((index1 start1 (+ index1 (the fixnum inc)))
- (index2 start2 (+ index2 (the fixnum inc))))
+ (index2 start2 (+ index2 (the fixnum inc))))
(())
(declare (fixnum index1 index2))
(if-mismatch (aref sequence1 index1) (pop sequence2))))
\f
(sb!xc:defmacro list-mumble-mismatch ()
`(do ((index1 start1 (+ index1 (the fixnum inc)))
- (index2 start2 (+ index2 (the fixnum inc))))
+ (index2 start2 (+ index2 (the fixnum inc))))
(())
(declare (fixnum index1 index2))
(if-mismatch (pop sequence1) (aref sequence2 index2))))
(sb!xc:defmacro list-list-mismatch ()
`(do ((sequence1 sequence1)
- (sequence2 sequence2)
- (index1 start1 (+ index1 (the fixnum inc)))
- (index2 start2 (+ index2 (the fixnum inc))))
+ (sequence2 sequence2)
+ (index1 start1 (+ index1 (the fixnum inc)))
+ (index2 start2 (+ index2 (the fixnum inc))))
(())
(declare (fixnum index1 index2))
(if-mismatch (pop sequence1) (pop sequence2))))
) ; EVAL-WHEN
-(defun mismatch (sequence1 sequence2 &key from-end (test #'eql) test-not
- (start1 0) end1 (start2 0) end2 key)
+(define-sequence-traverser mismatch
+ (sequence1 sequence2 &rest args &key from-end test test-not
+ start1 end1 start2 end2 key)
#!+sb-doc
- "The specified subsequences of Sequence1 and Sequence2 are compared
+ "The specified subsequences of SEQUENCE1 and SEQUENCE2 are compared
element-wise. If they are of equal length and match in every element, the
- result is Nil. Otherwise, the result is a non-negative integer, the index
- within Sequence1 of the leftmost position at which they fail to match; or,
+ result is NIL. Otherwise, the result is a non-negative integer, the index
+ within SEQUENCE1 of the leftmost position at which they fail to match; or,
if one is shorter than and a matching prefix of the other, the index within
- Sequence1 beyond the last position tested is returned. If a non-Nil
- :From-End keyword argument is given, then one plus the index of the
- rightmost position in which the sequences differ is returned."
+ SEQUENCE1 beyond the last position tested is returned. If a non-NIL
+ :FROM-END argument is given, then one plus the index of the rightmost
+ position in which the sequences differ is returned."
(declare (fixnum start1 start2))
- (let* ((length1 (length sequence1))
- (end1 (or end1 length1))
- (length2 (length sequence2))
- (end2 (or end2 length2)))
- (declare (type index length1 end1 length2 end2))
+ (declare (truly-dynamic-extent args))
+ (let* ((end1 (or end1 length1))
+ (end2 (or end2 length2)))
+ (declare (type index end1 end2))
(match-vars
(seq-dispatch sequence1
- (matchify-list (sequence1 start1 length1 end1)
- (seq-dispatch sequence2
- (matchify-list (sequence2 start2 length2 end2)
- (list-list-mismatch))
- (list-mumble-mismatch)))
(seq-dispatch sequence2
- (matchify-list (sequence2 start2 length2 end2)
- (mumble-list-mismatch))
- (mumble-mumble-mismatch))))))
+ (matchify-list (sequence1 start1 length1 end1)
+ (matchify-list (sequence2 start2 length2 end2)
+ (list-list-mismatch)))
+ (matchify-list (sequence1 start1 length1 end1)
+ (list-mumble-mismatch))
+ (apply #'sb!sequence:mismatch sequence1 sequence2 args))
+ (seq-dispatch sequence2
+ (matchify-list (sequence2 start2 length2 end2)
+ (mumble-list-mismatch))
+ (mumble-mumble-mismatch)
+ (apply #'sb!sequence:mismatch sequence1 sequence2 args))
+ (apply #'sb!sequence:mismatch sequence1 sequence2 args)))))
\f
;;; search comparison functions
(sb!xc:defmacro compare-elements (elt1 elt2)
`(if test-not
(if (funcall test-not (apply-key key ,elt1) (apply-key key ,elt2))
- (return nil)
- t)
+ (return nil)
+ t)
(if (not (funcall test (apply-key key ,elt1) (apply-key key ,elt2)))
- (return nil)
- t)))
+ (return nil)
+ t)))
(sb!xc:defmacro search-compare-list-list (main sub)
`(do ((main ,main (cdr main))
- (jndex start1 (1+ jndex))
- (sub (nthcdr start1 ,sub) (cdr sub)))
- ((or (null main) (null sub) (= (the fixnum end1) jndex))
- t)
- (declare (fixnum jndex))
- (compare-elements (car main) (car sub))))
+ (jndex start1 (1+ jndex))
+ (sub (nthcdr start1 ,sub) (cdr sub)))
+ ((or (endp main) (endp sub) (<= end1 jndex))
+ t)
+ (declare (type (integer 0) jndex))
+ (compare-elements (car sub) (car main))))
(sb!xc:defmacro search-compare-list-vector (main sub)
`(do ((main ,main (cdr main))
- (index start1 (1+ index)))
- ((or (null main) (= index (the fixnum end1))) t)
- (declare (fixnum index))
- (compare-elements (car main) (aref ,sub index))))
+ (index start1 (1+ index)))
+ ((or (endp main) (= index end1)) t)
+ (compare-elements (aref ,sub index) (car main))))
(sb!xc:defmacro search-compare-vector-list (main sub index)
`(do ((sub (nthcdr start1 ,sub) (cdr sub))
- (jndex start1 (1+ jndex))
- (index ,index (1+ index)))
- ((or (= (the fixnum end1) jndex) (null sub)) t)
- (declare (fixnum jndex index))
- (compare-elements (aref ,main index) (car sub))))
+ (jndex start1 (1+ jndex))
+ (index ,index (1+ index)))
+ ((or (<= end1 jndex) (endp sub)) t)
+ (declare (type (integer 0) jndex))
+ (compare-elements (car sub) (aref ,main index))))
(sb!xc:defmacro search-compare-vector-vector (main sub index)
`(do ((index ,index (1+ index))
- (sub-index start1 (1+ sub-index)))
- ((= sub-index (the fixnum end1)) t)
- (declare (fixnum sub-index index))
- (compare-elements (aref ,main index) (aref ,sub sub-index))))
+ (sub-index start1 (1+ sub-index)))
+ ((= sub-index end1) t)
+ (compare-elements (aref ,sub sub-index) (aref ,main index))))
(sb!xc:defmacro search-compare (main-type main sub index)
(if (eq main-type 'list)
`(seq-dispatch ,sub
- (search-compare-list-list ,main ,sub)
- (search-compare-list-vector ,main ,sub))
+ (search-compare-list-list ,main ,sub)
+ (search-compare-list-vector ,main ,sub)
+ ;; KLUDGE: just hack it together so that it works
+ (return-from search (apply #'sb!sequence:search sequence1 sequence2 args)))
`(seq-dispatch ,sub
- (search-compare-vector-list ,main ,sub ,index)
- (search-compare-vector-vector ,main ,sub ,index))))
+ (search-compare-vector-list ,main ,sub ,index)
+ (search-compare-vector-vector ,main ,sub ,index)
+ (return-from search (apply #'sb!sequence:search sequence1 sequence2 args)))))
) ; EVAL-WHEN
\f
(sb!xc:defmacro list-search (main sub)
`(do ((main (nthcdr start2 ,main) (cdr main))
- (index2 start2 (1+ index2))
- (terminus (- (the fixnum end2)
- (the fixnum (- (the fixnum end1)
- (the fixnum start1)))))
- (last-match ()))
+ (index2 start2 (1+ index2))
+ (terminus (- end2 (the (integer 0) (- end1 start1))))
+ (last-match ()))
((> index2 terminus) last-match)
- (declare (fixnum index2 terminus))
+ (declare (type (integer 0) index2))
(if (search-compare list main ,sub index2)
- (if from-end
- (setq last-match index2)
- (return index2)))))
+ (if from-end
+ (setq last-match index2)
+ (return index2)))))
(sb!xc:defmacro vector-search (main sub)
`(do ((index2 start2 (1+ index2))
- (terminus (- (the fixnum end2)
- (the fixnum (- (the fixnum end1)
- (the fixnum start1)))))
- (last-match ()))
+ (terminus (- end2 (the (integer 0) (- end1 start1))))
+ (last-match ()))
((> index2 terminus) last-match)
- (declare (fixnum index2 terminus))
+ (declare (type (integer 0) index2))
(if (search-compare vector ,main ,sub index2)
- (if from-end
- (setq last-match index2)
- (return index2)))))
+ (if from-end
+ (setq last-match index2)
+ (return index2)))))
) ; EVAL-WHEN
-(defun search (sequence1 sequence2 &key from-end (test #'eql) test-not
- (start1 0) end1 (start2 0) end2 key)
+(define-sequence-traverser search
+ (sequence1 sequence2 &rest args &key
+ from-end test test-not start1 end1 start2 end2 key)
(declare (fixnum start1 start2))
- (let ((end1 (or end1 (length sequence1)))
- (end2 (or end2 (length sequence2))))
+ (declare (truly-dynamic-extent args))
+ (let ((end1 (or end1 length1))
+ (end2 (or end2 length2)))
(seq-dispatch sequence2
- (list-search sequence2 sequence1)
- (vector-search sequence2 sequence1))))
+ (list-search sequence2 sequence1)
+ (vector-search sequence2 sequence1)
+ (apply #'sb!sequence:search sequence1 sequence2 args))))
+
+;;; FIXME: this was originally in array.lisp; it might be better to
+;;; put it back there, and make DOSEQUENCE and SEQ-DISPATCH be in
+;;; a new early-seq.lisp file.
+(defun fill-data-vector (vector dimensions initial-contents)
+ (let ((index 0))
+ (labels ((frob (axis dims contents)
+ (cond ((null dims)
+ (setf (aref vector index) contents)
+ (incf index))
+ (t
+ (unless (typep contents 'sequence)
+ (error "malformed :INITIAL-CONTENTS: ~S is not a ~
+ sequence, but ~W more layer~:P needed."
+ contents
+ (- (length dimensions) axis)))
+ (unless (= (length contents) (car dims))
+ (error "malformed :INITIAL-CONTENTS: Dimension of ~
+ axis ~W is ~W, but ~S is ~W long."
+ axis (car dims) contents (length contents)))
+ (sb!sequence:dosequence (content contents)
+ (frob (1+ axis) (cdr dims) content))))))
+ (frob 0 dimensions initial-contents))))