Simplify nbutlast
[jscl.git] / src / list.lisp
index 368d098..274d2f3 100644 (file)
   (when (< size 0)
     (error "Size must be non-negative"))
   (let ((newlist))
-    (do ((i 0))
-        ((= i size))
-      (push initial-element newlist)
-      (incf i))
-    newlist))
+    (dotimes (i size newlist)
+      (push initial-element newlist))))
 
 (defun map1 (func list)
   (with-collect
 
 (defun butlast (x &optional (n 1))
   "Returns x, less the n last elements in the list."
+  (nbutlast (copy-list x) n))
+
+(defun nbutlast (x &optional (n 1))
+  "Destructively returns x, less the n last elements in the list."
   (cond
-      ;; trivial optimizations
-    ((not x) x)
+    ((not (and (integerp n) (>= n 0)))
+     ;; TODO: turn this error into a type error, per CLHS spec.
+     (error "n must be a non-negative integer"))
+    ;; trivial optimizations
     ((zerop n) x)
-
-    ;; Base case
-    ((= n 1)
-     (and (consp (cdr x))
-          (cons (car x)
-                (butlast (cdr x) n))))
-    ;; O(n * (length x)) butlast for n > 1.
     (t
-     (let ((temp x))
-       (do
-        ((iter 0))
-        ;; quit when we reach the top or we ran out
-        ((or (= iter n)
-             (not temp)))
-         (setf temp (butlast temp 1))
-         (incf iter))
-       temp))))
+     ;; O(n) walk of the linked list, trimming out the link where appropriate
+     (let* ((head x)
+            (trailing (nthcdr n x)))
+       ;; If there are enough conses
+       (when (consp trailing)
+         (while (consp (cdr trailing))
+           (setq head (cdr head))
+           (setq trailing (cdr trailing)))
+         ;; snip
+         (rplacd head nil)
+         x)))))
 
 (defun member (x list &key key (test #'eql testp) (test-not #'eql test-not-p))
   (while list