0
(1- most-positive-fixnum))))
(mod #.sb!xc:most-positive-fixnum))
+ ;; Entries for {start,end}{,1,2}
,@(mapcan (lambda (names)
(destructuring-bind (start end length sequence) names
(list
`(,start
0
nil
- (if (<= 0 ,start ,length)
+ ;; Only evaluate LENGTH (which may be expensive)
+ ;; if START is non-NIL.
+ (if (or (zerop ,start) (<= 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)))))
+ `(,end
+ nil
+ nil
+ ;; Only evaluate LENGTH (which may be expensive)
+ ;; if END is non-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)))
(test-not nil
nil
(and test-not (%coerce-callable-to-fun test-not))
- (or null function))
- ))
+ (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))
+ (collect ((new-args)
+ (new-declarations)
+ ;; Things which are definitely used in any code path.
+ (rebindings/eager)
+ ;; Things which may be used/are only used in certain
+ ;; code paths (e.g. length).
+ (rebindings/lazy))
(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)))
+ ((sequence sequence1 sequence2)
+ (let* ((length-var (ecase arg
+ (sequence 'length)
+ (sequence1 'length1)
+ (sequence2 'length2)))
+ (cache-var (symbolicate length-var '#:-cache)))
+ (new-args arg)
+ (rebindings/eager `(,cache-var nil))
+ (rebindings/lazy
+ `(,length-var (truly-the
+ index
+ (or ,cache-var (setf ,cache-var (length ,arg))))))))
((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)))))))
+ (rebindings/eager `(,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))))
+ (rebindings/eager `(,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)))))
+ (symbol-macrolet (,@(rebindings/lazy))
+ (let* (,@(rebindings/eager))
+ (declare ,@(new-declarations))
+ ,@body
+ ))))))
;;; SEQ-DISPATCH does an efficient type-dispatch on the given SEQUENCE.
;;;
"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))
:type '(and list (satisfies list-length)))))
\f
+
+(defun emptyp (sequence)
+ #!+sb-doc
+ "Returns T if SEQUENCE is an empty sequence and NIL
+ otherwise. Signals an error if SEQUENCE is not a sequence."
+ (seq-dispatch sequence
+ (null sequence)
+ (zerop (length sequence))
+ (sb!sequence:emptyp sequence)))
+
(defun elt (sequence index)
#!+sb-doc "Return the element of SEQUENCE specified by INDEX."
(seq-dispatch sequence
"Return a sequence of the given TYPE and LENGTH, with elements initialized
to INITIAL-ELEMENT."
(declare (fixnum length))
- (let* ((adjusted-type
- (typecase type
+ (let* ((expanded-type (typexpand type))
+ (adjusted-type
+ (typecase expanded-type
(atom (cond
- ((eq type 'string) '(vector character))
- ((eq type 'simple-string) '(simple-array character (*)))
+ ((eq expanded-type 'string) '(vector character))
+ ((eq expanded-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)
+ ((eq (car expanded-type) 'string)
+ `(vector character ,@(cdr expanded-type)))
+ ((eq (car expanded-type) 'simple-string)
+ `(simple-array character ,(if (cdr expanded-type)
+ (cdr expanded-type)
'(*))))
- (t type)))
- (t type)))
+ (t type)))))
(type (specifier-type adjusted-type)))
(cond ((csubtypep type (specifier-type 'list))
(cond
\f
;;;; SUBSEQ
;;;;
+
+(define-array-dispatch vector-subseq-dispatch (array start end)
+ (declare (optimize speed (safety 0)))
+ (declare (type index start end))
+ (subseq array start 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 index start)
- (type (or null index) end))
+ (type (or null index) end)
+ (optimize speed))
(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))))))
+ (vector-subseq-dispatch data start end)))
(defun list-subseq* (sequence start end)
(declare (type list sequence)
(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."
+ "Destructively modifies SEQUENCE1 by copying successive elements
+into it from the SEQUENCE2.
+
+Elements are copied to the subseqeuence bounded by START1 and END1,
+from the subsequence bounded by START2 and END2. If these subsequences
+are not of the same length, then the shorter length determines how
+many elements are copied."
(declare (truly-dynamic-extent args))
(let* (;; KLUDGE: absent either rewriting FOO-REPLACE-FROM-BAR, or
;; excessively polluting DEFINE-SEQUENCE-TRAVERSER, we rebind
\f
;;;; CONCATENATE
-(defmacro sb!sequence:dosequence ((e sequence &optional return) &body body)
+(defmacro sb!sequence:dosequence ((element sequence &optional return) &body body)
+ #!+sb-doc
+ "Executes BODY with ELEMENT subsequently bound to each element of
+ SEQUENCE, then returns RETURN."
(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)
+ (dolist (,element ,sequence ,return) ,@body)
+ (do-vector-data (,element ,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))
+ (let ((,element nil))
,@(filter-dolist-declarations decls)
- ,e
+ ,element
,return))
- (let ((,e (funcall elt ,sequence state)))
+ (let ((,element (funcall elt ,sequence state)))
,@decls
(tagbody
,@forms))))))))))
((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))))
+ (unless (every #'emptyp sequences)
+ (sequence-type-length-mismatch-error
+ type (reduce #'+ sequences :key #'length))) ; FIXME: circular list issues.
+ '())
((cons-type-p type)
(multiple-value-bind (min exactp)
(sb!kernel::cons-type-length-info type)
(def %concatenate-to-string character)
(def %concatenate-to-base-string base-char))
\f
-;;;; MAP and MAP-INTO
+;;;; MAP
;;; helper functions to handle arity-1 subcases of MAP
(declaim (ftype (function (function sequence) list) %map-list-arity-1))
first-sequence
more-sequences))
-;;; KLUDGE: MAP has been rewritten substantially since the fork from
-;;; CMU CL in order to give reasonable performance, but this
-;;; implementation of MAP-INTO still has the same problems as the old
-;;; MAP code. Ideally, MAP-INTO should be rewritten to be efficient in
-;;; the same way that the corresponding cases of MAP have been
-;;; rewritten. Instead of doing it now, though, it's easier to wait
-;;; until we have DYNAMIC-EXTENT, at which time it should become
-;;; extremely easy to define a reasonably efficient MAP-INTO in terms
-;;; of (MAP NIL ..). -- WHN 20000920
+;;;; MAP-INTO
+
+(defmacro map-into-lambda (sequences params &body body)
+ (check-type sequences symbol)
+ `(flet ((f ,params ,@body))
+ (declare (truly-dynamic-extent #'f))
+ ;; Note (MAP-INTO SEQ (LAMBDA () ...)) is a different animal,
+ ;; hence the awkward flip between MAP and LOOP.
+ (if ,sequences
+ (apply #'map nil #'f ,sequences)
+ (loop (f)))))
+
+(define-array-dispatch vector-map-into (data start end fun sequences)
+ (declare (optimize speed (safety 0))
+ (type index start end)
+ (type function fun)
+ (type list sequences))
+ (let ((index start))
+ (declare (type index index))
+ (block mapping
+ (map-into-lambda sequences (&rest args)
+ (declare (truly-dynamic-extent args))
+ (when (eql index end)
+ (return-from mapping))
+ (setf (aref data index) (apply fun args))
+ (incf index)))
+ index))
+
+;;; Uses the machinery of (MAP NIL ...). For non-vectors we avoid
+;;; computing the length of the result sequence since we can detect
+;;; the end during mapping (if MAP even gets that far).
+;;;
+;;; For each result type, define a mapping function which is
+;;; responsible for replacing RESULT-SEQUENCE elements and for
+;;; terminating itself if the end of RESULT-SEQUENCE is reached.
+;;; The mapping function is defined with MAP-INTO-LAMBDA.
+;;;
+;;; MAP-INTO-LAMBDAs are optimized since they are the inner loops.
+;;; Because we are manually doing bounds checking with known types,
+;;; safety is turned off for vectors and lists but kept for generic
+;;; sequences.
(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))))
-
- (when fp-result
- (setf (fill-pointer result-sequence) len))
-
- (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))))))
+ (let ((really-fun (%coerce-callable-to-fun function)))
+ (etypecase result-sequence
+ (vector
+ (with-array-data ((data result-sequence) (start) (end)
+ ;; MAP-INTO ignores fill pointer when mapping
+ :check-fill-pointer nil)
+ (let ((new-end (vector-map-into data start end really-fun sequences)))
+ (when (array-has-fill-pointer-p result-sequence)
+ (setf (fill-pointer result-sequence) (- new-end start))))))
+ (list
+ (let ((node result-sequence))
+ (declare (type list node))
+ (map-into-lambda sequences (&rest args)
+ (declare (truly-dynamic-extent args)
+ (optimize speed (safety 0)))
+ (when (null node)
+ (return-from map-into result-sequence))
+ (setf (car node) (apply really-fun args))
+ (setf node (cdr node)))))
+ (sequence
+ (multiple-value-bind (iter limit from-end)
+ (sb!sequence:make-sequence-iterator result-sequence)
+ (map-into-lambda sequences (&rest args)
+ (declare (truly-dynamic-extent args) (optimize speed))
+ (when (sb!sequence:iterator-endp result-sequence
+ iter limit from-end)
+ (return-from map-into result-sequence))
+ (setf (sb!sequence:iterator-element result-sequence iter)
+ (apply really-fun args))
+ (setf iter (sb!sequence:iterator-step result-sequence
+ iter from-end)))))))
result-sequence)
\f
;;;; quantifiers
;; 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")))
+ (blockname (sb!xc:gensym "BLOCK"))
+ (wrapper (sb!xc:gensym "WRAPPER")))
(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)
+ (flet ((,wrapper (,@elements)
+ (declare (optimize (sb!c::check-tag-existence 0)))
+ (let ((pred-value (funcall ,pred ,@elements)))
+ (,',found-test pred-value
+ (return-from ,blockname
+ ,',found-result)))))
+ (declare (inline ,wrapper)
+ (dynamic-extent #',wrapper))
+ (map nil #',wrapper ,first-seq
+ ,@more-seqs))
,',unfound-result)))))))
(defquantifier some when pred-value :unfound-result nil :doc
"Apply PREDICATE to the 0-indexed elements of the sequences, then
(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)))
- (declare (type index start end))
- (seq-dispatch sequence
+ (declare (type index start)
+ (truly-dynamic-extent args))
+ (seq-dispatch sequence
+ (let ((end (or end length)))
+ (declare (type index end))
(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)))
+ initial-value ivp))))
+ (let ((end (or end length)))
+ (declare (type index end))
(if (= end start)
(if ivp initial-value (funcall function))
(if from-end
(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))))
+ initial-value aref)))))
+ (apply #'sb!sequence:reduce function sequence args)))
\f
;;;; DELETE
#!+sb-doc
"Return a sequence formed by destructively removing the specified ITEM from
the given SEQUENCE."
- (declare (fixnum start))
- (declare (truly-dynamic-extent args))
- (let ((end (or end length)))
- (declare (type index end))
- (seq-dispatch sequence
+ (declare (type fixnum start)
+ (truly-dynamic-extent args))
+ (seq-dispatch sequence
+ (let ((end (or end length)))
+ (declare (type index end))
(if from-end
(normal-list-delete-from-end)
- (normal-list-delete))
+ (normal-list-delete)))
+ (let ((end (or end length)))
+ (declare (type index end))
(if from-end
(normal-mumble-delete-from-end)
- (normal-mumble-delete))
- (apply #'sb!sequence:delete item sequence args))))
+ (normal-mumble-delete)))
+ (apply #'sb!sequence:delete item sequence args)))
(eval-when (:compile-toplevel :execute)
#!+sb-doc
"Return a sequence formed by destructively removing the elements satisfying
the specified PREDICATE from the given SEQUENCE."
- (declare (fixnum start))
- (declare (truly-dynamic-extent args))
- (let ((end (or end length)))
- (declare (type index end))
- (seq-dispatch sequence
+ (declare (type fixnum start)
+ (truly-dynamic-extent args))
+ (seq-dispatch sequence
+ (let ((end (or end length)))
+ (declare (type index end))
(if from-end
(if-list-delete-from-end)
- (if-list-delete))
+ (if-list-delete)))
+ (let ((end (or end length)))
+ (declare (type index end))
(if from-end
(if-mumble-delete-from-end)
- (if-mumble-delete))
- (apply #'sb!sequence:delete-if predicate sequence args))))
+ (if-mumble-delete)))
+ (apply #'sb!sequence:delete-if predicate sequence args)))
(eval-when (:compile-toplevel :execute)
#!+sb-doc
"Return a sequence formed by destructively removing the elements not
satisfying the specified PREDICATE from the given SEQUENCE."
- (declare (fixnum start))
- (declare (truly-dynamic-extent args))
- (let ((end (or end length)))
- (declare (type index end))
- (seq-dispatch sequence
+ (declare (type fixnum start)
+ (truly-dynamic-extent args))
+ (seq-dispatch sequence
+ (let ((end (or end length)))
+ (declare (type index end))
(if from-end
(if-not-list-delete-from-end)
- (if-not-list-delete))
+ (if-not-list-delete)))
+ (let ((end (or end length)))
+ (declare (type index end))
(if from-end
(if-not-mumble-delete-from-end)
- (if-not-mumble-delete))
- (apply #'sb!sequence:delete-if-not predicate sequence args))))
+ (if-not-mumble-delete)))
+ (apply #'sb!sequence:delete-if-not predicate sequence args)))
\f
;;;; REMOVE
#!+sb-doc
"Return a copy of SEQUENCE with elements satisfying the test (default is
EQL) with ITEM removed."
- (declare (fixnum start))
- (declare (truly-dynamic-extent args))
- (let ((end (or end length)))
- (declare (type index end))
- (seq-dispatch sequence
+ (declare (type fixnum start)
+ (truly-dynamic-extent args))
+ (seq-dispatch sequence
+ (let ((end (or end length)))
+ (declare (type index end))
(if from-end
(normal-list-remove-from-end)
- (normal-list-remove))
+ (normal-list-remove)))
+ (let ((end (or end length)))
+ (declare (type index end))
(if from-end
(normal-mumble-remove-from-end)
- (normal-mumble-remove))
- (apply #'sb!sequence:remove item sequence args))))
+ (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
"Return a copy of sequence with elements satisfying PREDICATE removed."
- (declare (fixnum start))
- (declare (truly-dynamic-extent args))
- (let ((end (or end length)))
- (declare (type index end))
- (seq-dispatch sequence
+ (declare (type fixnum start)
+ (truly-dynamic-extent args))
+ (seq-dispatch sequence
+ (let ((end (or end length)))
+ (declare (type index end))
(if from-end
(if-list-remove-from-end)
- (if-list-remove))
+ (if-list-remove)))
+ (let ((end (or end length)))
+ (declare (type index end))
(if from-end
(if-mumble-remove-from-end)
- (if-mumble-remove))
- (apply #'sb!sequence:remove-if predicate sequence args))))
+ (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
"Return a copy of sequence with elements not satisfying PREDICATE removed."
- (declare (fixnum start))
- (declare (truly-dynamic-extent args))
- (let ((end (or end length)))
- (declare (type index end))
- (seq-dispatch sequence
+ (declare (type fixnum start)
+ (truly-dynamic-extent args))
+ (seq-dispatch sequence
+ (let ((end (or end length)))
+ (declare (type index end))
(if from-end
(if-not-list-remove-from-end)
- (if-not-list-remove))
+ (if-not-list-remove)))
+ (let ((end (or end length)))
+ (declare (type index end))
(if from-end
(if-not-mumble-remove-from-end)
- (if-not-mumble-remove))
- (apply #'sb!sequence:remove-if-not predicate sequence args))))
+ (if-not-mumble-remove)))
+ (apply #'sb!sequence:remove-if-not predicate sequence args)))
\f
;;;; REMOVE-DUPLICATES
sequence is returned.
The :TEST-NOT argument is deprecated."
- (declare (fixnum start))
- (declare (truly-dynamic-extent args))
+ (declare (fixnum start)
+ (truly-dynamic-extent args))
(seq-dispatch sequence
(if sequence
(list-remove-duplicates* sequence test test-not
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))
+ (when 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)
(apply #'sb!sequence:delete-duplicates sequence args)))
\f
(sb!xc:defmacro subst-dispatch (pred)
`(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))
+ (let ((end (or end length)))
+ (declare (type index end))
+ (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)))
+
+ (let ((end (or end length)))
+ (declare (type index end))
+ (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*
#!+sb-doc
"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))
- (declare (truly-dynamic-extent args))
- (let ((end (or end length)))
- (declare (type index end))
- (subst-dispatch 'normal)))
+ (declare (type fixnum start)
+ (truly-dynamic-extent args))
+ (subst-dispatch 'normal))
\f
;;;; SUBSTITUTE-IF, SUBSTITUTE-IF-NOT
#!+sb-doc
"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 ((end (or end length))
- (test predicate)
+ (declare (type fixnum start)
+ (truly-dynamic-extent args))
+ (let ((test predicate)
(test-not nil)
old)
- (declare (type index length end))
(subst-dispatch 'if)))
(define-sequence-traverser substitute-if-not
#!+sb-doc
"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 ((end (or end length))
- (test predicate)
+ (declare (type fixnum start)
+ (truly-dynamic-extent args))
+ (let ((test predicate)
(test-not nil)
old)
- (declare (type index length end))
(subst-dispatch 'if-not)))
\f
;;;; NSUBSTITUTE
"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))
- (declare (truly-dynamic-extent args))
- (let ((end (or end length)))
- (seq-dispatch sequence
+ (declare (type fixnum start)
+ (truly-dynamic-extent args))
+ (seq-dispatch sequence
+ (let ((end (or end length)))
+ (declare (type index end))
(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)))
+ (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))
+ test test-not start end count key)))
+ (let ((end (or end length)))
+ (declare (type index end))
(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))))
+ 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))
"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))
- (declare (truly-dynamic-extent args))
- (let ((end (or end length)))
- (declare (fixnum end))
- (seq-dispatch sequence
+ (declare (type fixnum start)
+ (truly-dynamic-extent args))
+ (seq-dispatch sequence
+ (let ((end (or end length)))
+ (declare (type index end))
(if from-end
- (let ((length (length sequence)))
- (nreverse (nlist-substitute-if*
- new predicate (nreverse (the list sequence))
- (- length end) (- length start) count key)))
+ (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))
+ start end count key)))
+ (let ((end (or end length)))
+ (declare (type index end))
(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))))
+ 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))
+ (declare (type fixnum end)
+ (type function test)) ; coercion is done by caller
(do ((list (nthcdr start sequence) (cdr list))
(index start (1+ index)))
((or (= index end) (null list) (= count 0)) sequence)
(defun nvector-substitute-if* (new test sequence incrementer
start end count key)
+ (declare (type fixnum end)
+ (type function test)) ; coercion is done by caller
(do ((index start (+ index incrementer)))
((or (= index end) (= count 0)) sequence)
(when (funcall test (apply-key key (aref sequence index)))
"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))
- (declare (truly-dynamic-extent args))
- (let ((end (or end length)))
- (declare (fixnum end))
- (seq-dispatch sequence
+ (declare (type fixnum start)
+ (truly-dynamic-extent args))
+ (seq-dispatch sequence
+ (let ((end (or end length)))
+ (declare (fixnum end))
(if from-end
- (let ((length (length sequence)))
- (nreverse (nlist-substitute-if-not*
- new predicate (nreverse (the list sequence))
- (- length end) (- length start) count key)))
+ (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))
+ start end count key)))
+ (let ((end (or end length)))
+ (declare (fixnum end))
(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))))
+ 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))
+ (declare (type fixnum end)
+ (type function test)) ; coercion is done by caller
(do ((list (nthcdr start sequence) (cdr list))
(index start (1+ index)))
((or (= index end) (null list) (= count 0)) sequence)
(defun nvector-substitute-if-not* (new test sequence incrementer
start end count key)
+ (declare (type fixnum end)
+ (type function test)) ; coercion is done by caller
(do ((index start (+ index incrementer)))
((or (= index end) (= count 0)) sequence)
(when (not (funcall test (apply-key key (aref sequence index))))
(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 ()
+ (frobs (&optional bit-frob)
`(seq-dispatch sequence-arg
(frob sequence-arg from-end)
(with-array-data ((sequence sequence-arg :offset-var offset)
(end end)
:check-fill-pointer t)
(multiple-value-bind (f p)
- (macrolet ((frob2 () '(if from-end
- (frob sequence t)
- (frob sequence nil))))
+ (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))))
+ ,@(when bit-frob
+ `((simple-bit-vector
+ (if (and (typep item 'bit)
+ (eq #'identity key)
+ (or (eq #'eq test)
+ (eq #'eql test)
+ (eq #'equal test)))
+ (let ((p (%bit-position item sequence
+ from-end start end)))
+ (if p
+ (values item p)
+ (values nil nil)))
+ (vector*-frob sequence)))))
+ (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)
(vector*-frob (sequence)
`(%find-position-vector-macro item ,sequence
from-end start end key test)))
- (frobs)))
+ (frobs t)))
(defun %find-position-if (predicate sequence-arg from-end start end key)
(macrolet ((frob (sequence from-end)
`(%find-position-if predicate ,sequence
(pred sequence &rest args &key from-end start end key)
#!+sb-doc
"Return the number of elements in SEQUENCE satisfying PRED(el)."
- (declare (fixnum start))
- (declare (truly-dynamic-extent args))
- (let ((end (or end length))
- (pred (%coerce-callable-to-fun pred)))
- (declare (type index end))
+ (declare (type fixnum start)
+ (truly-dynamic-extent args))
+ (let ((pred (%coerce-callable-to-fun pred)))
(seq-dispatch sequence
- (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))
+ (let ((end (or end length)))
+ (declare (type index end))
+ (if from-end
+ (list-count-if nil t pred sequence)
+ (list-count-if nil nil pred sequence)))
+ (let ((end (or end length)))
+ (declare (type index end))
+ (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))))
(define-sequence-traverser count-if-not
(pred sequence &rest args &key from-end start end key)
#!+sb-doc
"Return the number of elements in SEQUENCE not satisfying TEST(el)."
- (declare (fixnum start))
- (declare (truly-dynamic-extent args))
- (let ((end (or end length))
- (pred (%coerce-callable-to-fun pred)))
- (declare (type index end))
+ (declare (type fixnum start)
+ (truly-dynamic-extent args))
+ (let ((pred (%coerce-callable-to-fun pred)))
(seq-dispatch sequence
- (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))
+ (let ((end (or end length)))
+ (declare (type index end))
+ (if from-end
+ (list-count-if t t pred sequence)
+ (list-count-if t nil pred sequence)))
+ (let ((end (or end length)))
+ (declare (type index end))
+ (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))))
(define-sequence-traverser count
#!+sb-doc
"Return the number of elements in SEQUENCE satisfying a test with ITEM,
which defaults to EQL."
- (declare (fixnum start))
- (declare (truly-dynamic-extent args))
+ (declare (type fixnum start)
+ (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))
- (let ((%test (if test-not-p
- (lambda (x)
- (not (funcall test-not item x)))
- (lambda (x)
- (funcall test item x)))))
- (seq-dispatch sequence
+ (let ((%test (if test-not-p
+ (lambda (x)
+ (not (funcall test-not item x)))
+ (lambda (x)
+ (funcall test item x)))))
+ (seq-dispatch sequence
+ (let ((end (or end length)))
+ (declare (type index end))
(if from-end
(list-count-if nil t %test sequence)
- (list-count-if nil nil %test sequence))
+ (list-count-if nil nil %test sequence)))
+ (let ((end (or end length)))
+ (declare (type index end))
(if from-end
(vector-count-if nil t %test sequence)
- (vector-count-if nil nil %test sequence))
- (apply #'sb!sequence:count item sequence args)))))
+ (vector-count-if nil nil %test sequence)))
+ (apply #'sb!sequence:count item sequence args))))
\f
;;;; MISMATCH
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))
+ (declare (type fixnum start1 start2))
(declare (truly-dynamic-extent args))
- (let* ((end1 (or end1 length1))
- (end2 (or end2 length2)))
- (declare (type index end1 end2))
- (match-vars
- (seq-dispatch sequence1
- (seq-dispatch sequence2
+ (seq-dispatch sequence1
+ (seq-dispatch sequence2
+ (let ((end1 (or end1 length1))
+ (end2 (or end2 length2)))
+ (declare (type index end1 end2))
+ (match-vars
(matchify-list (sequence1 start1 length1 end1)
(matchify-list (sequence2 start2 length2 end2)
- (list-list-mismatch)))
+ (list-list-mismatch)))))
+ (let ((end1 (or end1 length1))
+ (end2 (or end2 length2)))
+ (declare (type index end1 end2))
+ (match-vars
(matchify-list (sequence1 start1 length1 end1)
- (list-mumble-mismatch))
- (apply #'sb!sequence:mismatch sequence1 sequence2 args))
- (seq-dispatch sequence2
+ (list-mumble-mismatch))))
+ (apply #'sb!sequence:mismatch sequence1 sequence2 args))
+ (seq-dispatch sequence2
+ (let ((end1 (or end1 length1))
+ (end2 (or end2 length2)))
+ (declare (type index end1 end2))
+ (match-vars
(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)))))
+ (mumble-list-mismatch))))
+ (let ((end1 (or end1 length1))
+ (end2 (or end2 length2)))
+ (declare (type index end1 end2))
+ (match-vars
+ (mumble-mumble-mismatch)))
+ (apply #'sb!sequence:mismatch sequence1 sequence2 args))
+ (apply #'sb!sequence:mismatch sequence1 sequence2 args)))
+
\f
;;; search comparison functions
(define-sequence-traverser search
(sequence1 sequence2 &rest args &key
from-end test test-not start1 end1 start2 end2 key)
- (declare (fixnum start1 start2))
- (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)
- (apply #'sb!sequence:search sequence1 sequence2 args))))
+ (declare (type fixnum start1 start2)
+ (truly-dynamic-extent args))
+ (seq-dispatch sequence2
+ (let ((end1 (or end1 length1))
+ (end2 (or end2 length2)))
+ (declare (type index end1 end2))
+ (list-search sequence2 sequence1))
+ (let ((end1 (or end1 length1))
+ (end2 (or end2 length2)))
+ (declare (type index end1 end2))
+ (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