Reputation: 123
I want to apply the function (* x 2) to every other element in a list and return the entire list using the loop macro. The solution I've come up with so far is this:
(defun double-every-other (xs)
(loop for x in xs by #'cddr collect (* x 2)))
However, this will double every other element and only return the elements that were doubled, so if I executed:
(double-every-other '(1 2 3 4))
The result would be:
'(4 8)
But I want the result to be:
'(1 4 3 8)
Is there a way I can do this using (loop)?
Upvotes: 6
Views: 2982
Reputation: 21288
You could use the loop
"on" list iteration primitive. This takes a list of loop variables that will be "smeared" across the list, with the last being the tail of the entire remaining list. The conditional loop
for is necessary to avoid multiplying nil
if we have an odd number of arguments.
(defun double-every-other (list)
(loop for (single double tail) on list by #'cddr
if (null double)
collect single
else
append (list single (* 2 double))))
And if we try to run it:
* (double-every-other '(1 2 3 4 5))
(1 4 3 8 5)
Upvotes: 0
Reputation: 17849
another version, without loop at all:
(defun make-cycled (&rest items)
(setf (cdr (last items)) items))
(mapcar #'funcall
(make-cycled #'identity (lambda (x) (* 2 x)))
'(10 9 8 7 6 5 4 3))
;;=> (10 18 8 14 6 10 4 6)
Upvotes: 3
Reputation: 38967
Another version with less math:
(defun double-every-other (list)
(loop
for (a b) on list by #'cddr
collect a
when b collect (* b 2)))
(double-every-other '(1 2 3 4))
=> (1 4 3 8)
(double-every-other '(1 2 3 4 5))
=> (1 4 3 8 5)
Obviously, you won't be able to abstract the N as easily as the other answer (if you are thinking "macro", stop now). Here we iterate using the on
keyword, which means each sublist is visited in turn. Since we use by #'cddr
, every other sublist is skipped. The destructuring syntax (a b)
binds the first and second elements of the visited list.
Upvotes: 11
Reputation: 139401
(defun double-every-other (xs)
(loop for x in xs
for doublep = nil then (not doublep)
collect (if doublep (* x 2) x)))
Upvotes: 6
Reputation: 27434
You can for instance test an integer increasing while the list is scanned:
(defun double-every-other (xs)
(loop for x in xs
for i from 1
if (oddp i)
collect x
else collect (* x 2)))
Upvotes: 7