;;; Extensible sequences, based on the proposal by Christophe Rhodes. ;;;; This software is part of the SBCL system. See the README file for ;;;; more information. ;;;; This software is in the public domain and is provided with ;;;; absolutely no warranty. See the COPYING and CREDITS files for ;;;; more information. (cl:defpackage :sequence (:use) (:import-from :cl #:type-error-datum #:lambda #:&key #:&optional #:&rest #:&whole #:&body #:error #:null #:format #:class-of #:zerop #:multiple-value-bind #:cons #:cond #:and #:make-array #:array-element-type #:defvar #:defparameter #:make-list #:unless #:= #:let #:let* #:t #:make-array #:nil #:define-condition #:defun #:if #:eql #:setf #:defgeneric #:< #:rplacd #:nthcdr #:fill-pointer #:adjust-array #:declare #:ignore #:defmethod #:type-error #:apply #:array-total-size #:1- #:- #:array-has-fill-pointer-p #:>= #:or #:aref #:1+ #:values #:car #:cdr #:cddr #:last #:not #:<= #:loop #:eq #:do* #:typecase #:gensym #:push #:mapcar #:defmacro #:fdefinition #:complement #:functionp #:flet #:list* #:funcall #:setq #:incf #:when #:do #:identity #:return #:floor #:ceiling #:+ #:return-from #:> #:+ #:dotimes #:labels #:function #:ignorable #:prog1 #:minusp #:progn #:type #:the) (:export #:protocol-unimplemented #:protocol-unimplemented-operation #:merge #:with-sequence-iterator-functions #:make-sequence-like #:missing-arg #:emptyp #:length #:elt #:adjust-sequence #:make-sequence-iterator #:make-list-iterator #:make-vector-iterator #:make-simple-sequence-iterator #:iterator-step #:iterator-endp #:iterator-element #:iterator-index #:iterator-copy #:with-sequence-iterator #:find-if-not #:position #:position-if-not #:position-if #:subseq #:copy-seq #:fill #:nsubstitute #:nsubstitute-if #:nsubstitute-if-not #:substitute #:substitute-if #:substitute-if-not #:replace #:nreverse #:reverse #:concatenate #:reduce #:mismatch #:search #:delete #:delete-if #:delete-if-not #:remove #:remove-if #:remove-if-not #:delete-duplicates #:remove-duplicates #:sort #:stable-sort)) (uiop:define-package :gen-cl-user (:mix :sequence :cl)) (cl:in-package :sequence) (defun missing-arg () (error "A required &KEY or &OPTIONAL argument was not supplied.")) ;;;; basic protocol (define-condition protocol-unimplemented (type-error #+sbcl reference-condition) ((operation :initarg :operation :reader protocol-unimplemented-operation)) (:default-initargs :operation (missing-arg) :references '((:sbcl :node "Extensible Sequences"))) (:report (lambda (condition stream) (let ((operation (protocol-unimplemented-operation condition)) (datum (type-error-datum condition))) (format stream "~@" operation datum 'sequence (class-of datum))))) (:documentation "This error is signaled if a sequence operation is applied to an instance of a sequence class that does not support the operation.")) (defun protocol-unimplemented (operation sequence) (error 'protocol-unimplemented :datum sequence :expected-type '(or list vector) :operation operation)) (defgeneric emptyp (sequence) (:method ((s cl:list)) (null s)) (:method ((s cl:vector)) (zerop (cl:length s))) (:method ((s cl:sequence)) (zerop (cl:length s))) (:documentation "Returns T if SEQUENCE is an empty sequence and NIL otherwise. Signals an error if SEQUENCE is not a sequence.")) (defgeneric length (sequence) (:method ((s cl:list)) (cl:length s)) (:method ((s cl:vector)) (cl:length s)) (:method ((s cl:sequence)) (protocol-unimplemented 'length s)) (:documentation "Returns the length of SEQUENCE or signals a PROTOCOL-UNIMPLEMENTED error if the sequence protocol is not implemented for the class of SEQUENCE.")) (defgeneric elt (sequence index) (:method ((s cl:list) index) (cl:elt s index)) (:method ((s cl:vector) index) (cl:elt s index)) (:method ((s cl:sequence) index) (cl:declare (cl:ignore index)) (protocol-unimplemented 'elt s)) (:documentation "Returns the element at position INDEX of SEQUENCE or signals a PROTOCOL-UNIMPLEMENTED error if the sequence protocol is not implemented for the class of SEQUENCE.")) (defgeneric (setf elt) (new-value sequence index) (:argument-precedence-order sequence new-value index) (:method (new-value (s cl:list) index) (setf (cl:elt s index) new-value)) (:method (new-value (s cl:vector) index) (setf (cl:elt s index) new-value)) (:method (new-value (s cl:sequence) index) (cl:declare (cl:ignore index new-value)) (protocol-unimplemented '(setf elt) s)) (:documentation "Replaces the element at position INDEX of SEQUENCE with NEW-VALUE and returns NEW-VALUE or signals a PROTOCOL-UNIMPLEMENTED error if the sequence protocol is not implemented for the class of SEQUENCE.")) (defgeneric make-sequence-like (sequence length &key initial-element initial-contents) (:method ((s cl:list) length &key (initial-element nil iep) (initial-contents nil icp)) (cond ((and icp iep) (error "supplied both ~S and ~S to ~S" :initial-element :initial-contents 'make-sequence-like)) (iep (make-list length :initial-element initial-element)) (icp (unless (= (length initial-contents) length) (error "length mismatch in ~S" 'make-sequence-like)) (let ((result (make-list length))) (replace result initial-contents) result)) (t (make-list length)))) (:method ((s cl:vector) length &key (initial-element nil iep) (initial-contents nil icp)) (cond ((and icp iep) (error "supplied both ~S and ~S to ~S" :initial-element :initial-contents 'make-sequence-like)) (iep (make-array length :element-type (array-element-type s) :initial-element initial-element)) (icp (make-array length :element-type (array-element-type s) :initial-contents initial-contents)) (t (make-array length :element-type (array-element-type s))))) (:method ((s cl:sequence) length &key initial-element initial-contents) (cl:declare (cl:ignore initial-element initial-contents length)) (protocol-unimplemented 'make-sequence-like s)) (:documentation "Returns a freshly allocated sequence of length LENGTH and of the same class as SEQUENCE. Elements of the new sequence are initialized to INITIAL-ELEMENT, if supplied, initialized to INITIAL-CONTENTS if supplied, or identical to the elements of SEQUENCE if neither is supplied. Signals a PROTOCOL-UNIMPLEMENTED error if the sequence protocol is not implemented for the class of SEQUENCE.")) (defgeneric adjust-sequence (sequence length &key initial-element initial-contents) (:method ((s cl:list) length &key initial-element (initial-contents nil icp)) (if (eql length 0) nil (let ((olength (length s))) (cond ((eql length olength) (if icp (replace s initial-contents) s)) ((< length olength) (rplacd (nthcdr (1- length) s) nil) (if icp (replace s initial-contents) s)) ((null s) (let ((return (make-list length :initial-element initial-element))) (if icp (replace return initial-contents) return))) (t (rplacd (nthcdr (1- olength) s) (make-list (- length olength) :initial-element initial-element)) (if icp (replace s initial-contents) s)))))) (:method ((s cl:vector) length &rest args &key (initial-contents nil icp) initial-element) (declare (ignore initial-element)) (cond ((and (array-has-fill-pointer-p s) (>= (array-total-size s) length)) (setf (fill-pointer s) length) (if icp (replace s initial-contents) s)) ((eql (length s) length) (if icp (replace s initial-contents) s)) (t (apply #'adjust-array s length args)))) (:method ((s cl:sequence) length &rest args) (declare (ignore args length)) (protocol-unimplemented 'adjust-sequence s)) (:documentation "Return destructively modified SEQUENCE or a freshly allocated sequence of the same class as SEQUENCE of length LENGTH. Elements of the returned sequence are initialized to INITIAL-ELEMENT, if supplied, initialized to INITIAL-CONTENTS if supplied, or identical to the elements of SEQUENCE if neither is supplied. Signals a PROTOCOL-UNIMPLEMENTED error if the sequence protocol is not implemented for the class of SEQUENCE.")) ;;;; iterator protocol ;;; The general protocol (defgeneric make-sequence-iterator (sequence &key from-end start end) (:method ((s cl:vector) &key from-end (start 0) end) (make-vector-iterator s from-end start end)) (:method ((s cl:list) &key from-end (start 0) end) (make-list-iterator s from-end start end)) (:method ((s cl:sequence) &key from-end (start 0) end) (multiple-value-bind (iterator limit from-end) (make-simple-sequence-iterator s :from-end from-end :start start :end end) (values iterator limit from-end #'iterator-step #'iterator-endp #'iterator-element #'(setf iterator-element) #'iterator-index #'iterator-copy))) (:method ((s t) &key from-end start end) (declare (ignore from-end start end)) (error 'type-error :datum s :expected-type 'sequence)) (:documentation "Returns a sequence iterator for SEQUENCE or, if START and/or END are supplied, the subsequence bounded by START and END as nine values: 1. iterator state 2. limit 3. from-end 4. step function 5. endp function 6. element function 7. setf element function 8. index function 9. copy state function If FROM-END is NIL, the constructed iterator visits the specified elements in the order in which they appear in SEQUENCE. Otherwise, the elements are visited in the opposite order.")) ;;; magic termination value for list :from-end t (defvar *exhausted* (cons nil nil)) (defun make-list-iterator (list from-end start end) (multiple-value-bind (iterator limit from-end) (if from-end (let* ((termination (if (= start 0) *exhausted* (nthcdr (1- start) list))) (init (if (<= (or end (cl:length list)) start) termination (if end (last list (- (cl:length list) (1- end))) (last list))))) (values init termination t)) (cond ((not end) (values (nthcdr start list) nil nil)) (t (let ((st (nthcdr start list))) (values st (nthcdr (- end start) st) nil))))) (values iterator limit from-end (if from-end (lambda (list iterator from-end) (declare (ignore from-end)) (if (eq iterator list) *exhausted* (do* ((cdr list (cdr cdr))) ((eq (cdr cdr) iterator) cdr))) (1+ iterator)) (lambda (list iterator from-end) (declare (ignore list from-end)) (cdr iterator))) (lambda (list iterator limit from-end) (declare (ignore list from-end)) (eq iterator limit)) (lambda (list iterator) (declare (ignore list)) (car iterator)) (lambda (new-value list iterator) (declare (ignore list)) (setf (car iterator) new-value)) (lambda (list iterator) (loop for cdr on list for i from 0 when (eq cdr iterator) return i)) (lambda (list iterator) (declare (ignore list)) iterator)))) (defun make-vector-iterator (vector from-end start end) (let* ((end (or end (length vector))) (iterator (if from-end (1- end) start)) (limit (if from-end (1- start) end))) (values iterator limit from-end (if from-end (lambda (sequence iterator from-end) (declare (ignore sequence from-end)) (1- iterator)) (lambda (sequence iterator from-end) (declare (ignore sequence from-end)) (1+ iterator))) (lambda (sequence iterator limit from-end) (declare (ignore sequence from-end)) (= iterator limit)) (lambda (sequence iterator) (aref sequence iterator)) (lambda (new-value sequence iterator) (setf (aref sequence iterator) new-value)) (lambda (sequence iterator) (declare (ignore sequence)) iterator) (lambda (sequence iterator) (declare (ignore sequence)) iterator)))) ;;; the simple protocol: the simple iterator returns three values, ;;; STATE, LIMIT and FROM-END. (defgeneric make-simple-sequence-iterator (sequence &key from-end start end) (:method ((s cl:list) &key from-end (start 0) end) (if from-end (let* ((termination (if (= start 0) *exhausted* (nthcdr (1- start) s))) (init (if (<= (or end (length s)) start) termination (if end (last s (- (length s) (1- end))) (last s))))) (values init termination t)) (cond ((not end) (values (nthcdr start s) nil nil)) (t (let ((st (nthcdr start s))) (values st (nthcdr (- end start) st) nil)))))) (:method ((s cl:vector) &key from-end (start 0) end) (let ((end (or end (length s)))) (if from-end (values (1- end) (1- start) t) (values start end nil)))) (:method ((s cl:sequence) &key from-end (start 0) end) (let ((end (or end (length s)))) (if from-end (values (1- end) (1- start) from-end) (values start end nil)))) (:documentation "Returns a sequence iterator for SEQUENCE, START, END and FROM-END as three values: 1. iterator state 2. limit 3. from-end The returned iterator can be used with the generic iterator functions ITERATOR-STEP, ITERATOR-ENDP, ITERATOR-ELEMENT, (SETF ITERATOR-ELEMENT), ITERATOR-INDEX and ITERATOR-COPY.")) (defgeneric iterator-step (sequence iterator from-end) (:method ((s cl:list) iterator from-end) (if from-end (if (eq iterator s) *exhausted* (do* ((xs s (cdr xs))) ((eq (cdr xs) iterator) xs))) (cdr iterator))) (:method ((s cl:vector) iterator from-end) (if from-end (1- iterator) (1+ iterator))) (:method ((s cl:sequence) iterator from-end) (if from-end (1- iterator) (1+ iterator))) (:documentation "Moves ITERATOR one position forward or backward in SEQUENCE depending on the iteration direction encoded in FROM-END.")) (defgeneric iterator-endp (sequence iterator limit from-end) (:method ((s cl:list) iterator limit from-end) (declare (ignore from-end)) (eq iterator limit)) (:method ((s cl:vector) iterator limit from-end) (declare (ignore from-end)) (= iterator limit)) (:method ((s cl:sequence) iterator limit from-end) (declare (ignore from-end)) (= iterator limit)) (:documentation "Returns non-NIL when ITERATOR has reached LIMIT (which may correspond to the end of SEQUENCE) with respect to the iteration direction encoded in FROM-END.")) (defgeneric iterator-element (sequence iterator) (:method ((s cl:list) iterator) (car iterator)) (:method ((s cl:vector) iterator) (aref s iterator)) (:method ((s cl:sequence) iterator) (elt s iterator)) (:documentation "Returns the element of SEQUENCE associated to the position of ITERATOR.")) (defgeneric (setf iterator-element) (new-value sequence iterator) (:method (o (s cl:list) iterator) (setf (car iterator) o)) (:method (o (s cl:vector) iterator) (setf (aref s iterator) o)) (:method (o (s cl:sequence) iterator) (setf (elt s iterator) o)) (:documentation "Destructively modifies SEQUENCE by replacing the sequence element associated to position of ITERATOR with NEW-VALUE.")) (defgeneric iterator-index (sequence iterator) (:method ((s cl:list) iterator) ;; FIXME: this sucks. (In my defence, it is the equivalent of the ;; Apple implementation in Dylan...) (loop for l on s for i from 0 when (eq l iterator) return i)) (:method ((s cl:vector) iterator) iterator) (:method ((s cl:sequence) iterator) iterator) (:documentation "Returns the position of ITERATOR in SEQUENCE.")) (defgeneric iterator-copy (sequence iterator) (:method ((s cl:list) iterator) iterator) (:method ((s cl:vector) iterator) iterator) (:method ((s cl:sequence) iterator) iterator) (:documentation "Returns a copy of ITERATOR which also traverses SEQUENCE but can be mutated independently of ITERATOR.")) (defun %make-sequence-iterator (sequence from-end start end) (typecase sequence (cl:vector (make-vector-iterator sequence from-end start end)) (cl:list (make-list-iterator sequence from-end start end)) (t (make-sequence-iterator sequence :end end :start start :from-end from-end)))) (defmacro with-sequence-iterator ((&whole vars &optional iterator limit from-end-p step endp element set-element index copy) (sequence &key from-end (start 0) end) &body body) "Executes BODY with the elements of VARS bound to the iteration state returned by MAKE-SEQUENCE-ITERATOR for SEQUENCE and ARGS. Elements of VARS may be NIL in which case the corresponding value returned by MAKE-SEQUENCE-ITERATOR is ignored." (declare (ignore iterator limit from-end-p step endp element set-element index copy)) (let* ((ignored '()) (vars (mapcar (lambda (x) (or x (let ((name (gensym))) (push name ignored) name))) vars))) `(multiple-value-bind (,@vars) (%make-sequence-iterator ,sequence ,from-end ,start ,end) (declare (cl:type function ,@(nthcdr 3 vars)) (ignore ,@ignored)) ,@body))) (defmacro with-sequence-iterator-functions ((step endp elt setf index copy) (sequence &rest args &key from-end start end) &body body) "Executes BODY with the names STEP, ENDP, ELT, SETF, INDEX and COPY bound to local functions which execute the iteration state query and mutation functions returned by MAKE-SEQUENCE-ITERATOR for SEQUENCE and ARGS. STEP, ENDP, ELT, SETF, INDEX and COPY have dynamic extent." (declare (ignore from-end start end)) (let ((nstate (gensym "STATE")) (nlimit (gensym "LIMIT")) (nfrom-end (gensym "FROM-END-")) (nstep (gensym "STEP")) (nendp (gensym "ENDP")) (nelt (gensym "ELT")) (nsetf (gensym "SETF")) (nindex (gensym "INDEX")) (ncopy (gensym "COPY"))) `(with-sequence-iterator (,nstate ,nlimit ,nfrom-end ,nstep ,nendp ,nelt ,nsetf ,nindex ,ncopy) (,sequence,@args) (declare (cl:ignorable ,nstate ,nlimit ,nfrom-end ,nstep ,nendp ,nelt ,nsetf ,nindex ,ncopy)) (flet ((,step () (setq ,nstate (funcall ,nstep ,sequence,nstate ,nfrom-end))) (,endp () (funcall ,nendp ,sequence,nstate ,nlimit ,nfrom-end)) (,elt () (funcall ,nelt ,sequence,nstate)) (,setf (new-value) (funcall ,nsetf new-value ,sequence,nstate)) (,index () (funcall ,nindex ,sequence,nstate)) (,copy () (funcall ,ncopy ,sequence,nstate))) (declare (cl:dynamic-extent #',step #',endp #',elt #',setf #',index #',copy)) ,@body)))) (defmacro define-shadowing-generic (name (&rest args) &body options) (let* ((seq-position (cl:position 'sequence args)) (pre-args (cl:subseq args 0 seq-position)) (raw-post-args (cl:subseq args (1+ seq-position))) (cl-name (cl:intern (cl:symbol-name name) :cl)) (tail (cl:member #\& raw-post-args :key (lambda (x) (cl:elt (cl:symbol-name x) 0)))) (post-args (if (and tail (not (eql (car tail) '&optional))) `(,@(loop for x in raw-post-args for rest on raw-post-args while (not (eq rest tail)) collect x) &rest r) raw-post-args))) (unless seq-position (error "no sequence argument")) `(defgeneric ,name ,args (:method (,@pre-args (sequence cl:sequence) ,@post-args) (declare (cl:inline)) (apply #',cl-name ,@pre-args sequence ,@(cl:remove #\& post-args :key (lambda (x) (cl:elt (cl:symbol-name x) 0))))) ,@options))) (define-shadowing-generic find-if-not (pred sequence &key from-end start end key) (:argument-precedence-order sequence pred)) (define-shadowing-generic position (item sequence &key from-end start end test test-not key) (:argument-precedence-order sequence item)) (define-shadowing-generic position-if (pred sequence &key from-end start end key) (:argument-precedence-order sequence pred)) (define-shadowing-generic position-if-not (pred sequence &key from-end start end key) (:argument-precedence-order sequence pred)) (define-shadowing-generic subseq (sequence start &optional end)) (define-shadowing-generic copy-seq (sequence)) (define-shadowing-generic fill (sequence item &key start end)) (define-shadowing-generic nsubstitute (new old sequence &key start end from-end test test-not count key) (:argument-precedence-order sequence new old)) (define-shadowing-generic nsubstitute-if (new predicate sequence &key start end from-end count key) (:argument-precedence-order sequence new predicate)) (define-shadowing-generic nsubstitute-if-not (new predicate sequence &key start end from-end count key) (:argument-precedence-order sequence new predicate)) (define-shadowing-generic substitute (new old sequence &key start end from-end test test-not count key) (:argument-precedence-order sequence new old)) (define-shadowing-generic substitute-if (new predicate sequence &key start end from-end count key) (:argument-precedence-order sequence new predicate)) (define-shadowing-generic substitute-if-not (new predicate sequence &key start end from-end count key) (:argument-precedence-order sequence new predicate)) (defun %sequence-replace (sequence1 sequence2 start1 end1 start2 end2) (with-sequence-iterator (state1 limit1 from-end1 step1 endp1 elt1 setelt1) (sequence1 :start start1 :end end1) (declare (ignore elt1)) (with-sequence-iterator (state2 limit2 from-end2 step2 endp2 elt2) (sequence2 :start start2 :end end2) (do () ((or (funcall endp1 sequence1 state1 limit1 from-end1) (funcall endp2 sequence2 state2 limit2 from-end2)) sequence1) (funcall setelt1 (funcall elt2 sequence2 state2) sequence1 state1) (setq state1 (funcall step1 sequence1 state1 from-end1)) (setq state2 (funcall step2 sequence2 state2 from-end2)))))) (defgeneric replace (sequence1 sequence2 &key start1 end1 start2 end2) (:argument-precedence-order sequence2 sequence1)) (define-shadowing-generic nreverse (sequence)) (define-shadowing-generic reverse (sequence)) (defgeneric concatenate (result-prototype &rest sequences) (:documentation "Implements CL:CONCATENATE for extended sequences. RESULT-PROTOTYPE corresponds to the RESULT-TYPE of CL:CONCATENATE but receives a prototype instance of an extended sequence class instead of a type specifier. By dispatching on RESULT-PROTOTYPE, methods on this generic function specify how extended sequence classes act when they are specified as the result type in a CL:CONCATENATE call. RESULT-PROTOTYPE may not be fully initialized and thus should only be used for dispatch and to determine its class.")) (define-shadowing-generic reduce (function sequence &key from-end start end initial-value) (:argument-precedence-order sequence function)) (defgeneric mismatch (sequence1 sequence2 &key from-end start1 end1 start2 end2 test test-not key)) (defgeneric search (sequence1 sequence2 &key from-end start1 end1 start2 end2 test test-not key)) (define-shadowing-generic delete (item sequence &key from-end test test-not start end count key) (:argument-precedence-order sequence item)) (define-shadowing-generic delete-if (predicate sequence &key from-end start end count key) (:argument-precedence-order sequence predicate)) (define-shadowing-generic delete-if-not (predicate sequence &key from-end start end count key) (:argument-precedence-order sequence predicate)) (define-shadowing-generic remove (item sequence &key from-end test test-not start end count key) (:argument-precedence-order sequence item)) (define-shadowing-generic remove-if (predicate sequence &key from-end start end count key) (:argument-precedence-order sequence predicate)) (define-shadowing-generic remove-if-not (predicate sequence &key from-end start end count key) (:argument-precedence-order sequence predicate)) (define-shadowing-generic delete-duplicates (sequence &key from-end test test-not start end key)) (define-shadowing-generic remove-duplicates (sequence &key from-end test test-not start end key)) (define-shadowing-generic sort (sequence predicate &key key)) (define-shadowing-generic stable-sort (sequence predicate &key key)) (defgeneric merge (result-prototype sequence1 sequence2 predicate &key key) (:documentation "Implements CL:MERGE for extended sequences. RESULT-PROTOTYPE corresponds to the RESULT-TYPE of CL:MERGE but receives a prototype instance of an extended sequence class instead of a type specifier. By dispatching on RESULT-PROTOTYPE, methods on this generic function specify how extended sequence classes act when they are specified as the result type in a CL:MERGE call. RESULT-PROTOTYPE may not be fully initialized and thus should only be used for dispatch and to determine its class. Another difference to CL:MERGE is that PREDICATE is a function, not a function designator.")) (defun %coerce-callable-to-fun (callable) (cl:etypecase callable (function callable) (cl:symbol (cl:symbol-function callable)))) (defmethod sequence:merge ((result-prototype cl:sequence) (sequence1 cl:sequence) (sequence2 cl:sequence) (predicate cl:function) &key key) (let ((key-function (when key (%coerce-callable-to-fun key))) (result (sequence:make-sequence-like result-prototype (+ (length sequence1) (length sequence2)))) endp1 elt1 key1 endp2 elt2 key2) (sequence:with-sequence-iterator-functions (step-result endp-result elt-result setelt-result index-result copy-result) (result) ;; TODO allow nil and fewer number of elements (declare (ignorable #'endp-result #'elt-result #'copy-result)) (sequence:with-sequence-iterator-functions (step1 endp1 elt1 setelt1 index1 copy1) (sequence1) (declare (ignorable #'setelt1 #'copy1)) (sequence:with-sequence-iterator-functions (step2 endp2 elt2 setelt2 index2 copy2) (sequence2) (declare (ignorable #'setelt2 #'copy2)) (labels ((pop/no-key1 () (unless (setf endp1 (endp1)) (setf elt1 (elt1)))) (pop/no-key2 () (unless (setf endp2 (endp2)) (setf elt2 (elt2)))) (pop/key1 () (unless (setf endp1 (endp1)) (setf key1 (funcall (the function key-function) (setf elt1 (elt1)))))) (pop/key2 () (unless (setf endp2 (endp2)) (setf key2 (funcall (the function key-function) (setf elt2 (elt2)))))) (pop-one/no-key () (if (funcall predicate elt2 elt1) ; see comment in MERGE-LIST* (prog1 elt2 (step2) (pop/no-key2)) (prog1 elt1 (step1) (pop/no-key1)))) (pop-one/key () (if (funcall predicate key2 key1) (prog1 elt2 (step2) (pop/key2)) (prog1 elt1 (step1) (pop/key1))))) (declare (cl:dynamic-extent #'pop/no-key1 #'pop/no-key2 #'pop/key1 #'pop/key2 #'pop-one/no-key #'pop-one/key)) ;; Populate ENDP{1,2}, ELT{1,2} and maybe KEY{1,2}. (cond (key-function (pop/key1) (pop/key2)) (t (pop/no-key1) (pop/no-key2))) (loop with pop-one = (if key-function #'pop-one/key #'pop-one/no-key) do (cond (endp2 ; batch-replace rest of SEQUENCE1 if SEQUENCE2 exhausted (unless endp1 (replace result sequence1 :start1 (index-result) :start2 (index1))) (return)) (endp1 (unless endp2 (replace result sequence2 :start1 (index-result) :start2 (index2))) (return)) (t (setelt-result (funcall pop-one)) (step-result)))))))) result))