Sometimes I need to loop through consecutive pairs in a list. The way I do it right now is
(loop for x on lst while (not (null (cdr x)))
(do something on (car x) and (cadr x)))
I'm wondering if there is a better/built-in way to do this.
The reason I need this is sometimes I want, e.g. some function that add consecutive pairs
(1 2 3 4 5) ----> (3 5 7 9)
Is there any built-in function like reduce which allow me to get this?
AFAIK, there isn't a built-in function to do what you want. You could try to put something together with maplist
, but my first instinct would be to reach for loop
too.
Just a couple of notes on what you've got there though. First, (not (null foo))
is equivalent to foo
in CL, since a non-NIL
value is treated as t
by boolean operations. Second, loop
can destructure its arguments, meaning you can write this more elegantly as
(loop for (a b) on lst while b
collect (+ a b))
The maplist
version would look something like
(maplist
(lambda (rest)
(when (cdr rest)
(+ (first rest) (second rest)))
lst)
which I consider less readable (this would also return NIL as the last element of its result, rather than just ending before that).
I believe Paul Graham has a function named map-tuple in OnLisp which does this. It can move down the list by cdr, cddr, or however you please. Here is a commented version of it. It uses his anaphoric if macro aif.
(defun map-tuple (someList f &optional (by #'cdr))
"(map-tuple someList f &optional (by #'cdr))
f is a function that takes two args, returns one val (maybe)
the map-tuple will collect all non-nil results in a list.
example:
(map-tuple '(1 2 3 4 5 6) (lambda (a b) (format T \"a: ~A b:~A~%\" a b)) #'cdr)
a: 1 b:2
a: 2 b:3
a: 3 b:4
a: 4 b:5
a: 5 b:6
a: 6 b:NIL
(map-tuple '(1 2 3 4 5 6) (lambda (a b) (format T \"a: ~A b:~A~%\" a b)) #'cddr)
a: 1 b:2
a: 3 b:4
a: 5 b:6
"
(cond ((null someList)
nil)
(T
(aif (funcall f (car someList) (cadr someList))
(cons it (map-tuple (funcall by someList) f by))
(map-tuple (funcall by someList) f by)))))