1 ;;;; -*- Mode: Lisp; Syntax: ANSI-Common-Lisp; indent-tabs-mode: nil -*-
5 ;;; This code was based on Arthur Lemmens' in
6 ;;; <URL:http://groups.google.com/groups?as_umsgid=39F36F1A.B8F19D20%40simplex.nl>;
9 (in-package :iolib.base
)
11 (defun split-sequence (delimiter sequence
&key
(start 0) (end nil
) (from-end nil
)
12 (count nil
) (remove-empty-subseqs nil
)
13 (test #'eql
) (test-not nil
) (key #'identity
))
14 "Return a list of subsequences in seq delimited by delimiter.
16 If :remove-empty-subseqs is NIL, empty subsequences will be included
17 in the result; otherwise they will be discarded. All other keywords
18 work analogously to those for CL:SUBSTITUTE. In particular, the
19 behaviour of :from-end is possibly different from other versions of
20 this function; :from-end values of NIL and T are equivalent unless
21 :count is supplied. The second return value is an index suitable as an
22 argument to CL:SUBSEQ into the sequence indicating where processing
24 (check-bounds sequence start end
)
26 ((and (not from-end
) (null test-not
))
27 (split-from-start (lambda (sequence start
)
28 (position delimiter sequence
:start start
:key key
:test test
))
29 sequence start end count remove-empty-subseqs
))
30 ((and (not from-end
) test-not
)
31 (split-from-start (lambda (sequence start
)
32 (position delimiter sequence
:start start
:key key
:test-not test-not
))
33 sequence start end count remove-empty-subseqs
))
34 ((and from-end
(null test-not
))
35 (split-from-end (lambda (sequence end
)
36 (position delimiter sequence
:end end
:from-end t
:key key
:test test
))
37 sequence start end count remove-empty-subseqs
))
38 ((and from-end test-not
)
39 (split-from-end (lambda (sequence end
)
40 (position delimiter sequence
:end end
:from-end t
:key key
:test-not test-not
))
41 sequence start end count remove-empty-subseqs
))))
43 (defun split-sequence-if (predicate sequence
&key
(start 0) (end nil
) (from-end nil
)
44 (count nil
) (remove-empty-subseqs nil
) (key #'identity
))
45 "Return a list of subsequences in seq delimited by items satisfying
48 If :remove-empty-subseqs is NIL, empty subsequences will be included
49 in the result; otherwise they will be discarded. All other keywords
50 work analogously to those for CL:SUBSTITUTE-IF. In particular, the
51 behaviour of :from-end is possibly different from other versions of
52 this function; :from-end values of NIL and T are equivalent unless
53 :count is supplied. The second return value is an index suitable as an
54 argument to CL:SUBSEQ into the sequence indicating where processing
56 (check-bounds sequence start end
)
58 (split-from-end (lambda (sequence end
)
59 (position-if predicate sequence
:end end
:from-end t
:key key
))
60 sequence start end count remove-empty-subseqs
)
61 (split-from-start (lambda (sequence start
)
62 (position-if predicate sequence
:start start
:key key
))
63 sequence start end count remove-empty-subseqs
)))
65 (defun split-sequence-if-not (predicate sequence
&key
(count nil
) (remove-empty-subseqs nil
)
66 (from-end nil
) (start 0) (end nil
) (key #'identity
))
67 "Return a list of subsequences in seq delimited by items satisfying
68 \(CL:COMPLEMENT predicate).
70 If :remove-empty-subseqs is NIL, empty subsequences will be included
71 in the result; otherwise they will be discarded. All other keywords
72 work analogously to those for CL:SUBSTITUTE-IF-NOT. In particular,
73 the behaviour of :from-end is possibly different from other versions
74 of this function; :from-end values of NIL and T are equivalent unless
75 :count is supplied. The second return value is an index suitable as an
76 argument to CL:SUBSEQ into the sequence indicating where processing
78 (check-bounds sequence start end
)
80 (split-from-end (lambda (sequence end
)
81 (position-if-not predicate sequence
:end end
:from-end t
:key key
))
82 sequence start end count remove-empty-subseqs
)
83 (split-from-start (lambda (sequence start
)
84 (position-if-not predicate sequence
:start start
:key key
))
85 sequence start end count remove-empty-subseqs
)))
87 (defun split-from-end (position-fn sequence start end count remove-empty-subseqs
)
89 :for right
:= end
:then left
90 :for left
:= (max (or (funcall position-fn sequence right
) -
1)
92 :unless
(and (= right
(1+ left
))
93 remove-empty-subseqs
) ; empty subseq we don't want
94 :if
(and count
(>= nr-elts count
))
95 ;; We can't take any more. Return now.
96 :return
(values (nreverse subseqs
) right
)
98 :collect
(subseq sequence
(1+ left
) right
) into subseqs
99 :and
:sum
1 :into nr-elts
100 :until
(< left start
)
101 :finally
(return (values (nreverse subseqs
) (1+ left
)))))
103 (defun split-from-start (position-fn sequence start end count remove-empty-subseqs
)
104 (let ((length (length sequence
)))
106 :for left
:= start
:then
(+ right
1)
107 :for right
:= (min (or (funcall position-fn sequence left
) length
)
109 :unless
(and (= right left
)
110 remove-empty-subseqs
) ; empty subseq we don't want
111 :if
(and count
(>= nr-elts count
))
112 ;; We can't take any more. Return now.
113 :return
(values subseqs left
)
115 :collect
(subseq sequence left right
) :into subseqs
116 :and
:sum
1 :into nr-elts
117 :until
(>= right end
)
118 :finally
(return (values subseqs right
)))))