X-Git-Url: http://repo.macrolet.net/gitweb/?a=blobdiff_plain;f=src%2Fcode%2Fseq.lisp;h=50eec6bfc78a3d170d13529b9baea9463cf4d667;hb=15d6e7c9a2c3234f95dfe278046fa2fee1b0c007;hp=4593efb56fbd625dd4225aa26f3a1a79f01126a0;hpb=1bbb76fcfb9baddf0dc96412c87575d8aeb69c6d;p=sbcl.git diff --git a/src/code/seq.lisp b/src/code/seq.lisp index 4593efb..50eec6b 100644 --- a/src/code/seq.lisp +++ b/src/code/seq.lisp @@ -61,11 +61,23 @@ '((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 t) + (parse-body body :doc-string-allowed t) (collect ((new-args) (new-declarations) (adjustments)) (dolist (arg args) (case arg @@ -203,8 +215,6 @@ ;; This seems silly, is there something better? '(integer 0 (0)))))) -(declaim (ftype (function (sequence index index) nil) - signal-bounding-indices-bad-error)) (defun signal-bounding-indices-bad-error (sequence start end) (let ((length (length sequence))) (error 'bounding-indices-bad-error @@ -254,9 +264,23 @@ (defun make-sequence (type length &key (initial-element nil iep)) #!+sb-doc "Return a sequence of the given TYPE and LENGTH, with elements initialized - to :INITIAL-ELEMENT." + 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)) (cond ((type= type (specifier-type 'list)) @@ -267,42 +291,42 @@ (if (= length 0) 'nil (sequence-type-length-mismatch-error type length))) - ((csubtypep (specifier-type '(cons nil t)) type) - ;; The above is quite a neat way of finding out if - ;; there's a type restriction on the CDR of the - ;; CONS... if there is, I think it's probably fair to - ;; give up; if there isn't, then the list to be made - ;; must have a length of more than 0. - (if (> length 0) - (make-list length :initial-element initial-element) - (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)) - (if (typep type 'array-type) - ;; KLUDGE: the above test essentially asks "Do we know - ;; what the upgraded-array-element-type is?" [consider - ;; (OR STRING BIT-VECTOR)] - (progn - (aver (= (length (array-type-dimensions type)) 1)) - (let ((etype (type-specifier + (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)))) - (sequence-type-too-hairy (type-specifier 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))))) (t (bad-sequence-type-error (type-specifier type)))))) ;;;; SUBSEQ @@ -449,6 +473,8 @@ (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)) @@ -457,6 +483,8 @@ (= 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))))) @@ -581,7 +609,7 @@ (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 @@ -607,17 +635,17 @@ (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))) + `(do ((1st (cdr ,list) (if (endp 1st) 1st (cdr 1st))) (2nd ,list 1st) (3rd '() 2nd)) ((atom 2nd) 3rd) @@ -713,19 +741,20 @@ (and (vectorp x) (= (length x) 0)))) sequences) 'nil - (sequence-type-length-mismatch-error type - ;; FIXME: circular - ;; list issues. And - ;; rightward-drift. - (reduce #'+ - (mapcar #'length - sequences))))) - ((csubtypep (specifier-type '(cons nil t)) type) - (if (notevery (lambda (x) (or (null x) - (and (vectorp x) (= (length x) 0)))) - sequences) - (apply #'concat-to-list* sequences) - (sequence-type-length-mismatch-error type 0))) + (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))) + (apply #'concat-to-list* sequences)))) (t (sequence-type-too-hairy (type-specifier type))))) ((csubtypep type (specifier-type 'vector)) (apply #'concat-to-simple* output-type-spec sequences)) @@ -1235,7 +1264,7 @@ ) ; EVAL-WHEN (define-sequence-traverser delete - (item sequence &key from-end (test #'eql) test-not start + (item sequence &key from-end test test-not start end count key) #!+sb-doc "Return a sequence formed by destructively removing the specified ITEM from @@ -1454,7 +1483,7 @@ ) ; EVAL-WHEN (define-sequence-traverser remove - (item sequence &key from-end (test #'eql) test-not start + (item sequence &key from-end test test-not start end count key) #!+sb-doc "Return a copy of SEQUENCE with elements satisfying the test (default is @@ -1520,16 +1549,20 @@ (declare (fixnum index)) (setq splice (cdr (rplacd splice (list (car current))))) (setq current (cdr current))) - (do ((index 0 (1+ index))) + (do ((index start (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))) + (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)) @@ -1538,7 +1571,9 @@ ()) (declare (fixnum i)) (if (if test-not - (not (funcall test-not it (apply-key key (car l)))) + (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)))))) @@ -1564,12 +1599,20 @@ (do ((elt)) ((= index end)) (setq elt (aref vector index)) + ;; FIXME: Relying on POSITION allowing both :TEST and :TEST-NOT + ;; arguments simultaneously is a little fragile, since ANSI says + ;; we can't depend on it, so we need to remember to keep that + ;; extension in our implementation. It'd probably be better to + ;; rewrite this to avoid passing both (as + ;; LIST-REMOVE-DUPLICATES* was rewritten ca. sbcl-0.7.12.18). (unless (or (and from-end - (position (apply-key key elt) result :start start - :end jndex :test test :test-not test-not :key key)) + (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))) + (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))) (setq index (1+ index))) @@ -1581,9 +1624,9 @@ (shrink-vector result jndex))) (define-sequence-traverser remove-duplicates - (sequence &key (test #'eql) test-not (start 0) end from-end key) + (sequence &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. @@ -1650,7 +1693,7 @@ (setq jndex (1+ jndex))))) (define-sequence-traverser delete-duplicates - (sequence &key (test #'eql) test-not (start 0) end from-end key) + (sequence &key test test-not start end from-end key) #!+sb-doc "The elements of SEQUENCE are examined, and if any two match, one is discarded. The resulting sequence, which may be formed by destroying the @@ -1761,7 +1804,7 @@ ) ; EVAL-WHEN (define-sequence-traverser substitute - (new old sequence &key from-end (test #'eql) test-not + (new old sequence &key from-end test test-not start count end key) #!+sb-doc "Return a sequence of the same kind as SEQUENCE with the same elements, @@ -1775,26 +1818,28 @@ ;;;; SUBSTITUTE-IF, SUBSTITUTE-IF-NOT (define-sequence-traverser substitute-if - (new test sequence &key from-end start end count key) + (new pred sequence &key from-end start end count key) #!+sb-doc "Return a sequence of the same kind as SEQUENCE with the same elements - except that all elements satisfying the TEST are replaced with NEW. See + except that all elements satisfying the PRED are replaced with NEW. See manual for details." (declare (fixnum start)) (let ((end (or end length)) + (test pred) test-not old) (declare (type index length end)) (subst-dispatch 'if))) (define-sequence-traverser substitute-if-not - (new test sequence &key from-end start end count key) + (new pred sequence &key from-end start end count key) #!+sb-doc "Return a sequence of the same kind as SEQUENCE with the same elements - except that all elements not satisfying the TEST are replaced with NEW. + except that all elements not satisfying the PRED are replaced with NEW. See manual for details." (declare (fixnum start)) (let ((end (or end length)) + (test pred) test-not old) (declare (type index length end)) @@ -1803,7 +1848,7 @@ ;;;; NSUBSTITUTE (define-sequence-traverser nsubstitute - (new old sequence &key from-end (test #'eql) test-not + (new old sequence &key from-end test test-not end count key start) #!+sb-doc "Return a sequence of the same kind as SEQUENCE with the same elements @@ -1855,10 +1900,10 @@ ;;;; NSUBSTITUTE-IF, NSUBSTITUTE-IF-NOT (define-sequence-traverser nsubstitute-if - (new test sequence &key from-end start end count key) + (new pred sequence &key from-end start end count key) #!+sb-doc "Return a sequence of the same kind as SEQUENCE with the same elements - except that all elements satisfying the TEST are replaced with NEW. + except that all elements satisfying the PRED are replaced with NEW. SEQUENCE may be destructively modified. See manual for details." (declare (fixnum start)) (let ((end (or end length))) @@ -1867,14 +1912,14 @@ (if from-end (let ((length (length sequence))) (nreverse (nlist-substitute-if* - new test (nreverse (the list sequence)) + new pred (nreverse (the list sequence)) (- length end) (- length start) count key))) - (nlist-substitute-if* new test sequence + (nlist-substitute-if* new pred sequence start end count key)) (if from-end - (nvector-substitute-if* new test sequence -1 + (nvector-substitute-if* new pred sequence -1 (1- end) (1- start) count key) - (nvector-substitute-if* new test sequence 1 + (nvector-substitute-if* new pred sequence 1 start end count key))))) (defun nlist-substitute-if* (new test sequence start end count key) @@ -1895,7 +1940,7 @@ (setq count (1- count))))) (define-sequence-traverser nsubstitute-if-not - (new test sequence &key from-end start end count key) + (new pred sequence &key from-end start end count key) #!+sb-doc "Return a sequence of the same kind as SEQUENCE with the same elements except that all elements not satisfying the TEST are replaced with NEW. @@ -1907,14 +1952,14 @@ (if from-end (let ((length (length sequence))) (nreverse (nlist-substitute-if-not* - new test (nreverse (the list sequence)) + new pred (nreverse (the list sequence)) (- length end) (- length start) count key))) - (nlist-substitute-if-not* new test sequence + (nlist-substitute-if-not* new pred sequence start end count key)) (if from-end - (nvector-substitute-if-not* new test sequence -1 + (nvector-substitute-if-not* new pred sequence -1 (1- end) (1- start) count key) - (nvector-substitute-if-not* new test sequence 1 + (nvector-substitute-if-not* new pred sequence 1 start end count key))))) (defun nlist-substitute-if-not* (new test sequence start end count key) @@ -1959,10 +2004,10 @@ (frob sequence nil)))) (typecase sequence (simple-vector (frob2)) - (simple-string (frob2)) + (simple-base-string (frob2)) (t (vector*-frob sequence)))) (declare (type (or index null) p)) - (values f (and p (the index (+ p offset)))))))))) + (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 @@ -2046,22 +2091,22 @@ ) ; EVAL-WHEN -(define-sequence-traverser count-if (test sequence &key from-end start end key) +(define-sequence-traverser count-if (pred sequence &key from-end start end key) #!+sb-doc - "Return the number of elements in SEQUENCE satisfying TEST(el)." + "Return the number of elements in SEQUENCE satisfying PRED(el)." (declare (fixnum start)) (let ((end (or end length))) (declare (type index end)) (seq-dispatch sequence (if from-end - (list-count-if nil t test sequence) - (list-count-if nil nil test sequence)) + (list-count-if nil t pred sequence) + (list-count-if nil nil pred sequence)) (if from-end - (vector-count-if nil t test sequence) - (vector-count-if nil nil test sequence))))) + (vector-count-if nil t pred sequence) + (vector-count-if nil nil pred sequence))))) (define-sequence-traverser count-if-not - (test sequence &key from-end start end key) + (pred sequence &key from-end start end key) #!+sb-doc "Return the number of elements in SEQUENCE not satisfying TEST(el)." (declare (fixnum start)) @@ -2069,11 +2114,11 @@ (declare (type index end)) (seq-dispatch sequence (if from-end - (list-count-if t t test sequence) - (list-count-if t nil test sequence)) + (list-count-if t t pred sequence) + (list-count-if t nil pred sequence)) (if from-end - (vector-count-if t t test sequence) - (vector-count-if t nil test sequence))))) + (vector-count-if t t pred sequence) + (vector-count-if t nil pred sequence))))) (define-sequence-traverser count (item sequence &key from-end start end @@ -2179,7 +2224,7 @@ (define-sequence-traverser mismatch (sequence1 sequence2 - &key from-end (test #'eql) test-not + &key from-end test test-not start1 end1 start2 end2 key) #!+sb-doc "The specified subsequences of SEQUENCE1 and SEQUENCE2 are compared @@ -2224,32 +2269,30 @@ `(do ((main ,main (cdr main)) (jndex start1 (1+ jndex)) (sub (nthcdr start1 ,sub) (cdr sub))) - ((or (null main) (null sub) (= (the fixnum end1) jndex)) + ((or (endp main) (endp sub) (<= end1 jndex)) t) - (declare (fixnum jndex)) - (compare-elements (car main) (car sub)))) + (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)))) + ((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)))) + ((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 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) @@ -2269,12 +2312,10 @@ (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))))) + (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) @@ -2282,12 +2323,10 @@ (sb!xc:defmacro vector-search (main sub) `(do ((index2 start2 (1+ index2)) - (terminus (- (the fixnum end2) - (the fixnum (- (the fixnum end1) - (the fixnum start1))))) + (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) @@ -2297,7 +2336,7 @@ (define-sequence-traverser search (sequence1 sequence2 - &key from-end (test #'eql) test-not + &key from-end test test-not start1 end1 start2 end2 key) (declare (fixnum start1 start2)) (let ((end1 (or end1 length1))