Reputation: 286
((1 2 3)
(2 3 4)
(3 4 5)
(4 5 6))
from
(1 2 3 4 5 6)
And what is the type of such operation?
What I tried:
(loop
:with l2 = '()
:with l1 = '(1 2 3 4 5 6)
:for i :in l1
:do (push (subseq l1 0 3) l2))
Upvotes: 0
Views: 346
Reputation: 10010
Alternatively use map
:
(let ((l '(1 2 3 4 5 6)))
(map 'list #'list l (cdr l) (cddr l)))
;; ((1 2 3) (2 3 4) (3 4 5) (4 5 6))
You can read it as:
l
with values (1 2 3 4 5 6)
map
over the list and its two successive cdr
s#'list
on the elements of the lists map
is looping through in parallel'list
@WillNess suggested even simpler:
(let ((l '(1 2 3 4 5 6)))
(mapcar #'list l (cdr l) (cddr l)))
thanks! So then we could generalize using only map
variants:
(defun subseqs-of-n (l n)
(apply #'mapcar #'list (subseq (maplist #'identity l) 0 n)))
(maplist #'identity l)
is equivalent to (loop for sl on l collect sl)
.
However,
(loop for sl on l
for i from 0 to n
collect sl)
is better because it stops at n-th round of looping ...
Upvotes: 4
Reputation: 782407
You're pushing the same sublist every time through the loop.
You can use :for sublist on
to loop over successive tails of a list.
And use :collect
to make a list of all the results, rather than pushing onto your own list
(loop
:for l1 on '(1 2 3 4 5 6)
:if (>= (length l1) 3)
:collect (subseq l1 0 3)
:else
:do (loop-finish))
Upvotes: 4
Reputation: 10010
Or the classical more C
-like for-loop-ing with indexes to solve it.
But use it more on strings/vectors but less on lists, because its performance is
credits and thanks to @WillNess who pointed both points out (see comments below).
(defun subseqs-of-n (ls n) ;; works on strings, too!
(loop :for i :from 0 :to (- (length ls) n)
:collect (subseq ls i (+ i n))))
So on vectors/strings use:
(subseqs-of-n "gattaca" 5)
;; ("gatta" "attac" "ttaca")
Upvotes: 1
Reputation: 139401
First let's define a function take-n
, which either returns n items or an empty list, if there are not enough items. It will not scan the whole list.
(defun take-n (n list)
(loop repeat n
when (null list) return (values nil nil)
collect (pop list)))
Then we move this function take-n
over the list until it returns NIL
.
(defun moving-slice (n list)
(loop for l on list
for p = (take-n n l)
while p
collect p))
Example:
CL-USER 207 > (moving-slice 3 '(1 2))
NIL
CL-USER 208 > (moving-slice 3 '(1 2 3))
((1 2 3))
CL-USER 209 > (moving-slice 3 '(1 2 3 4 5 6 7))
((1 2 3) (2 3 4) (3 4 5) (4 5 6) (5 6 7))
Upvotes: 3
Reputation:
Here's a version of Barmar's answer (which should be the accepted one) which is a bit more general and only calls length
once.
(defun successive-leading-parts (l n)
(loop repeat (1+ (- (length l) n))
for lt on l
collect (subseq lt 0 n)))
> (successive-leading-parts '(1 2 3 4) 3)
((1 2 3) (2 3 4))
> (successive-leading-parts '(1 2 3 4) 2)
((1 2) (2 3) (3 4))
Upvotes: 2