X-Git-Url: http://repo.macrolet.net/gitweb/?a=blobdiff_plain;f=src%2Fsequence.lisp;h=880f7fd6edfb19ac755710a64f0d119c3dec5c8a;hb=266509b078969a40bded783057fc15a873c75723;hp=42e7795d4f2f530f068fca920bb5db41b2f35333;hpb=10335c06c6bbffbad74e9a4aa218fba923d1f6f4;p=jscl.git diff --git a/src/sequence.lisp b/src/sequence.lisp index 42e7795..880f7fd 100644 --- a/src/sequence.lisp +++ b/src/sequence.lisp @@ -13,93 +13,96 @@ ;; You should have received a copy of the GNU General Public License ;; along with JSCL. If not, see . -(defmacro do-sequence (iteration &body body) - (let ((seq (gensym)) - (index (gensym))) - `(let ((,seq ,(second iteration))) - (cond - ;; Strings - ((stringp ,seq) - (let ((,index 0)) - (dotimes (,index (length ,seq)) - (let ((,(first iteration) - (char ,seq ,index))) - ,@body)))) - ;; Lists - ((listp ,seq) - (dolist (,(first iteration) ,seq) - ,@body)) - (t - (error "type-error!")))))) - -(defmacro doseq ((elt seq &optional index) &body body) - (let* ((nseq (gensym "seq")) - (i (or index (gensym "i"))) - (list-body (if index - `(let ((,i -1)) - (dolist (,elt ,nseq) - (incf ,i) - ,@body)) - `(dolist (,elt ,nseq) - ,@body)))) +(/debug "loading sequence.lisp!") + +(defun sequencep (thing) + (or (listp thing) (vectorp thing))) + +(defun not-seq-error (thing) + (error "`~S' is not of type SEQUENCE" thing)) + +(defmacro do-sequence ((elt seq &optional (index (gensym "i") index-p)) &body body) + (let ((nseq (gensym "seq"))) + (unless (symbolp elt) + (error "`~S' must be a symbol." elt)) `(let ((,nseq ,seq)) (if (listp ,nseq) - ,list-body - (dotimes (,i (length ,nseq)) - (let ((,elt (aref ,nseq ,i))) + ,(if index-p + `(let ((,index -1)) + (dolist (,elt ,nseq) + (incf ,index) + ,@body)) + `(dolist (,elt ,nseq) + ,@body)) + (dotimes (,index (length ,nseq)) + (let ((,elt (aref ,nseq ,index))) ,@body)))))) -(defun find (item seq &key key (test #'eql)) +(defun find (item seq &key key (test #'eql testp) (test-not #'eql test-not-p)) + (do-sequence (x seq) + (when (satisfies-test-p item x :key key :test test :testp testp + :test-not test-not :test-not-p test-not-p) + (return x)))) + +(defun find-if (predicate sequence &key key) (if key - (doseq (x seq) - (when (funcall test (funcall key x) item) + (do-sequence (x sequence) + (when (funcall predicate (funcall key x)) (return x))) - (doseq (x seq) - (when (funcall test x item) + (do-sequence (x sequence) + (when (funcall predicate x) (return x))))) -(defun find-if (predicate sequence &key (key #'identity)) - (do-sequence (x sequence) - (when (funcall predicate (funcall key x)) - (return x)))) - -(defun some (function seq) - (do-sequence (elt seq) - (when (funcall function elt) - (return-from some t)))) - -(defun every (function seq) - (do-sequence (elt seq) - (unless (funcall function elt) - (return-from every nil))) - t) +(defun position (elt sequence + &key key (test #'eql testp) + (test-not #'eql test-not-p) + (start 0) end) + ;; TODO: Implement START and END efficiently for all the sequence + ;; functions. + (let ((end (or end (length sequence)))) + (do-sequence (x sequence index) + (when (and (<= start index) + (< index end) + (satisfies-test-p elt x + :key key :test test :testp testp + :test-not test-not :test-not-p test-not-p)) + (return index))))) -(defun position (elt sequence) - (let ((pos 0)) - (do-sequence (x seq) - (when (eq elt x) - (return)) - (incf pos)) - pos)) +;; TODO: need to support &key from-end +(defun position-if (predicate sequence + &key key (start 0) end) + ;; TODO: Implement START and END efficiently for all the sequence + ;; functions. + (let ((end (or end (length sequence)))) + (do-sequence (x sequence index) + (when (and (<= start index) + (< index end) + (funcall predicate (if key (funcall key x) x))) + (return index))))) +(defun position-if-not (predicate sequence + &key key (start 0) end) + (position-if (complement predicate) sequence :key key :start start :end end)) -(defun remove (x seq) +(defun remove (x seq &key key (test #'eql testp) (test-not #'eql test-not-p)) (cond ((null seq) nil) ((listp seq) (let* ((head (cons nil nil)) (tail head)) - (doseq (elt seq) - (unless (eql x elt) + (do-sequence (elt seq) + (unless (satisfies-test-p x elt :key key :test test :testp testp + :test-not test-not :test-not-p test-not-p) (let ((new (list elt))) (rplacd tail new) (setq tail new)))) (cdr head))) (t (let (vector) - (doseq (elt seq index) - (if (eql x elt) + (do-sequence (elt seq index) + (if (satisfies-test-p x elt :key key :test test :testp testp + :test-not test-not :test-not-p test-not-p) ;; Copy the beginning of the vector only when we find an element ;; that does not match. (unless vector @@ -111,29 +114,161 @@ (or vector seq))))) -;;; TODO: Support vectors +(defun some (function seq) + (do-sequence (elt seq) + (when (funcall function elt) + (return-from some t)))) -(defun remove-if (func list) +(defun every (function seq) + (do-sequence (elt seq) + (unless (funcall function elt) + (return-from every nil))) + t) + +(defun remove-if (func seq) (cond - ((null list) - nil) - ((funcall func (car list)) - (remove-if func (cdr list))) - (t - ;; - (cons (car list) (remove-if func (cdr list)))))) + ((listp seq) (list-remove-if func seq nil)) + ((arrayp seq) (vector-remove-if func seq nil)) + (t (not-seq-error seq)))) -(defun remove-if-not (func list) +(defun remove-if-not (func seq) (cond - ((null list) - nil) - ((funcall func (car list)) - (cons (car list) (remove-if-not func (cdr list)))) - (t - (remove-if-not func (cdr list))))) + ((listp seq) (list-remove-if func seq t)) + ((arrayp seq) (vector-remove-if func seq t)) + (t (not-seq-error seq)))) + +(defun list-remove-if (func list negate) + (if (endp list) + () + (let ((test (funcall func (car list)))) + (if (if negate (not test) test) + (list-remove-if func (cdr list) negate) + (cons (car list) (list-remove-if func (cdr list) negate)))))) + +(defun vector-remove-if (func vector negate) + (let ((out-vector (make-array 0))) + (do-sequence (element vector i) + (let ((test (funcall func element))) + (when (if negate test (not test)) + (vector-push-extend element out-vector)))) + out-vector)) (defun subseq (seq a &optional b) - (if b - (slice seq a b) - (slice seq a))) + (cond + ((listp seq) + (if b + (let ((diff (- b a))) + (cond + ((zerop diff) ()) + ((minusp diff) + (error "Start index must be smaller than end index")) + (t + (let* ((drop-a (copy-list (nthcdr a seq))) + (pointer drop-a)) + (dotimes (_ (1- diff)) + (setq pointer (cdr pointer)) + (when (null pointer) + (error "Ending index larger than length of list"))) + (rplacd pointer ()) + drop-a)))) + (copy-list (nthcdr a seq)))) + ((vectorp seq) + (let* ((b (or b (length seq))) + (size (- b a)) + (new (make-array size :element-type (array-element-type seq)))) + (do ((i 0 (1+ i)) + (j a (1+ j))) + ((= j b) new) + (aset new i (aref seq j))))) + (t (not-seq-error seq)))) + +(defun copy-seq (sequence) + (subseq sequence 0)) + + +;;; Reduce (based on SBCL's version) + +(defun reduce (function sequence &key key from-end (start 0) end (initial-value nil ivp)) + (let ((key (or key #'identity)) + (end (or end (length sequence)))) + (if (= end start) + (if ivp initial-value (funcall function)) + (macrolet ((reduce-list (function sequence key start end initial-value ivp from-end) + `(let ((sequence + ,(if from-end + `(reverse (nthcdr ,start ,sequence)) + `(nthcdr ,start ,sequence)))) + (do ((count (if ,ivp ,start (1+ ,start)) + (1+ count)) + (sequence (if ,ivp sequence (cdr sequence)) + (cdr sequence)) + (value (if ,ivp ,initial-value (funcall ,key (car sequence))) + ,(if from-end + `(funcall ,function (funcall ,key (car sequence)) value) + `(funcall ,function value (funcall ,key (car sequence)))))) + ((>= count ,end) value))))) + (if from-end + (reduce-list function sequence key start end initial-value ivp t) + (reduce-list function sequence key start end initial-value ivp nil)))))) + +(defun elt (sequence index) + (when (< index 0) + (error "The index ~D is below zero." index)) + (etypecase sequence + (list + (let ((i 0)) + (dolist (elt sequence) + (when (eql index i) + (return-from elt elt)) + (incf i)) + (error "The index ~D is too large for ~A of length ~D." index 'list i))) + (array + (let ((length (length sequence))) + (when (>= index length) + (error "The index ~D is too large for ~A of length ~D." index 'vector length)) + (aref sequence index))))) + +(defun mismatch (sequence1 sequence2 &key key (test #'eql testp) (test-not nil test-not-p) + (start1 0) (end1 (length sequence1)) + (start2 0) (end2 (length sequence2))) + (let ((index1 start1) + (index2 start2)) + (while (and (<= index1 end1) (<= index2 end2)) + (when (or (eql index1 end1) (eql index2 end2)) + (return-from mismatch (if (eql end1 end2) NIL index1))) + (unless (satisfies-test-p (elt sequence1 index1) (elt sequence2 index2) + :key key :test test :testp testp + :test-not test-not :test-not-p test-not-p) + (return-from mismatch index1)) + (incf index1) + (incf index2)))) + +(defun list-search (sequence1 list2 args) + (let ((length1 (length sequence1)) + (position 0)) + (while list2 + (let ((mismatch (apply #'mismatch sequence1 list2 args))) + (when (or (not mismatch) (>= mismatch length1)) + (return-from list-search position))) + (pop list2) + (incf position)))) + +(defun vector-search (sequence1 vector2 args) + (let ((length1 (length sequence1))) + (dotimes (position (length vector2)) + (let ((mismatch (apply #'mismatch sequence1 (subseq vector2 position) args))) + (when (or (not mismatch) (>= mismatch length1)) + (return-from vector-search position)))))) +(defun search (sequence1 sequence2 &rest args &key key test test-not) + (unless (sequencep sequence1) + (not-seq-error sequence1)) + (when (or (and (listp sequence1) (null sequence1)) + (and (vectorp sequence1) (zerop (length sequence1)))) + (return-from search 0)) + (funcall + (typecase sequence2 + (list #'list-search) + (array #'vector-search) + (t (not-seq-error sequence2))) + sequence1 sequence2 args))