I have a sorted list of integers, (1 2 4 5 6 6 7 8 10 10 10)
. I want to group them all, so that I get ((1) (2) (4) (5) (6 6) (7) (8) (10 10 10))
.
So far I have this, which works:
(let ((current-group (list)) (groups (list)))
(dolist (n *sorted*)
(when (and (not (null current-group)) (not (eql (first current-group) n)))
(push current-group groups)
(setf current-group (list)))
(push n current-group))
(push current-group groups)
(nreverse groups))
But I'm sure there must be a much more LISPy way to do this. Any ideas?
There's already an accepted answer, but I think it's worth looking at another way of decomposing this problem, although the approach here is essentially the same). First, let's define cut that takes a list and a predicate, and returns the prefix and suffix of the list, where the suffix begins with the first element of the list that satisfies the predicate, and the prefix is everything before that that didn't:
Then, using cut, we can move down the an input list taking prefixes and suffixes with a predicate that's checking whether an element is not eql to the first element of the list. That is, beginning with (1 1 1 2 3 3) you'd cut with the predicate checking for "not eql to 1", to get (1 1 1) and (2 3 3). You'd add the first to the list of groups, and the second becomes the new tail.
On implementing
cut
I tried to make that cut relatively efficient, insofar as it only makes one pass through the list. Since
member
returns the entire tail of the list that begins with the found element, you can actually usemember
with:test-not
to get the tail that you want:Then, you can use ldiff to return the prefix that comes before that tail:
It's a simple matter, then, to combine the approaches and to return the tail and the prefix as multiples values. This gives a version of
cut
that takes only the list as an argument, and might be easier to understand (but it's a bit less efficient).Not that bad. I would write it this way:
I like to use
reduce
:or using
push
: