[cl-utilities-devel] split-sequence performance problem.

Szymon ssbm2 at o2.pl
Fri May 5 21:51:59 UTC 2006


Peter Scott wrote:

> [.....] If it's not an issue but just a glaring
> performance wart, it might be better to leave the code as it is,
> simply because it's well tested and debugged in its current state and
> stability is very important to me. [.....]

Leave the code as it is, I just wrote an utility for splitting lists
and it's ok for me.

Regards, Szymon.

ps. utility works like this:

CL-USER> (split-list-if #'zerop '(0 0))
NIL

CL-USER> (split-list-if #'zerop '(0 0) :preserve-delimiters t)
((0 0))

(split-list-if #'zerop '(0 0 0 x) :preserve-delimiters t)

CL-USER> (split-list-if #'null '(a nil b))
((A) (B))

CL-USER> (split-list-if #'null '(nil a nil nil b nil))
((A) (B))

CL-USER> (split-list-if #'null '(nil a nil nil b nil)
                        :preserve-delimiters t)
((NIL) (A) (NIL NIL) (B) (NIL))

CL-USER> (split-list-if #'null '(nil a nil nil b nil) :count 2)
((A) (B))

CL-USER> (split-list-if #'null '(nil a nil nil b nil)
                        :preserve-delimiters t
                        :count 2)
((NIL) (A))

CL-USER> (split-list-if #'numberp '(0 a 1 2 b 3 4 c d))
((A) (B) (C D))

CL-USER> (split-list-if #'numberp '(0 a 1 2 b 3 4 c d) :preserve-delimiters t)
((0) (A) (1 2) (B) (3 4) (C D))

CL-USER> (split-list-if #'symbolp '(0 a 1 2 b 3 4 c d) :preserve-delimiters t)
((0) (A) (1 2) (B) (3 4) (C D))

CL-USER> (split-list-if #'numberp
                        '(foo (0 1) bar (2 3) baz 4 mug 5)
                        :key (lambda (x) (if (consp x) (car x) x)))
((FOO) (BAR) (BAZ) (MUG))

CL-USER> (split-list-if #'numberp
                        '(foo (0 1) bar (2 3) baz 4 mug 5)
                        :key (lambda (x) (if (consp x) (car x) x))
                        :preserve-delimiters t)
((FOO) ((0 1)) (BAR) ((2 3)) (BAZ) (4) (MUG) (5))



CL-USER> (split-list-if #'numberp '(0 a 1 2 b 3 4 c d 0 0 x)
                        :preserve-delimiters t
                        :count 3
                        :from-end t)
((C D) (0 0) (X))

CL-USER> (split-list-if #'numberp '(0 a 1 2 b 3 4 c d 0 0 x)
                        :count 3
                        :from-end t)
((B) (C D) (X))

|#

(defun split-list-if (test list
                      &key preserve-delimiters key count from-end
                      &aux (ldiff/cons (if (and from-end count) #'cons #'ldiff)))
  (when (or (null list)
            (and count (zerop count)))
    (return-from split-list-if))
  (when (and from-end (not count))
    (setq from-end nil))
  (multiple-value-bind (member member-not)
      (values (lambda (list) (member-if test list :key key))
              (let ((test-not (complement test)))
                (lambda (list) (member-if test-not list :key key))))
    (let ((get-next
           (if preserve-delimiters
               (let ((f member))
                 (lambda ()
                   (let ((result-begin list)
                         (result-end (funcall f list)))
                     (setq f (if (eq f member) member-not member))
                     (setq list result-end)
                     (when result-begin (funcall ldiff/cons result-begin result-end)))))
             (lambda (&aux (start (funcall member-not list))
                           (tail (funcall member start)))
               (when start (funcall ldiff/cons start (setq list tail)))))))
      (let (result pointer next init-delims)
        (setq init-delims
              (let ((tail (funcall member-not list)))
                (cond ((and (null tail) (cdr list))
                       (prog1 (copy-list list) (setq list nil)))
                      (t
                       (prog1 (ldiff list tail) (setq list tail))))))
        (if preserve-delimiters
            (when (and init-delims
                       (or (and (not from-end) count (= count 1))
                           (null list)))
              (return-from split-list-if (list init-delims)))
          (unless list (return-from split-list-if)))
        (setq result (list (funcall get-next)) pointer result)
        (when (and init-delims preserve-delimiters)
          (setq result (nconc (list init-delims) result))
          (when count (decf count)))
        (if count
            (loop repeat (1- count)
                  while (setq next (funcall get-next))
                  do (setq pointer (cdr (rplacd pointer (list next)))))
          (loop while (setq next (funcall get-next))
                do (setq pointer (cdr (rplacd pointer (list next))))))
        (when (and count from-end)
          (when list
            (cond ((= count 1)
                   (loop for x = (funcall get-next) do (if x (setq next x) (return)))
                   (setq result (rplaca result next)))
                  (t (loop while (setq next (funcall get-next))
                           for cell = (prog1 result (setq result (cdr result)))
                           do (rplaca (setq pointer (cdr (rplacd pointer cell))) next))
                     (rplacd pointer nil))))
          (map-into result (lambda (cons) (ldiff (car cons) (cdr cons))) result))
        result))))



More information about the cl-utilities-devel mailing list