2016-08-07 4 views
3

Я хочу применить функцию (* x 2) к каждому другому элементу в списке и вернуть весь список, используя цикл . Решение, которое я придумал до сих пор это:Common Lisp - применить функцию к любому другому элементу в списке

(defun double-every-other (xs) 
    (loop for x in xs by #'cddr collect (* x 2))) 

Однако это будет удваиваться каждый элемент и возвращает только те элементы, которые были в два раза, так что, если я выполнил:

(double-every-other '(1 2 3 4)) 

результат будет выглядеть так:

'(4 8) 

Но я хочу, чтобы результат:

'(1 4 3 8) 

Есть ли способ, которым я могу это сделать, используя (цикл)?

ответ

5

Вы можете, например, проверить целое число увеличивается, а список сканируется:

(defun double-every-other (xs) 
    (loop for x in xs 
    for i from 1 
    if (oddp i) 
    collect x 
    else collect (* x 2))) 
7

Другой вариант с меньшим количеством математике:

(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) 

Очевидно, что вы не будете в состоянии абстрагироваться от N так же легко, как и другой ответ (если вы думаете «макрос», остановитесь сейчас). Здесь мы итерации используем ключевое слово on, что означает, что каждый подсписок посещается по очереди. Поскольку мы используем by #'cddr, каждый другой подсписок пропускается. Синтаксис деструктурирования (a b) связывает первый и второй элементы посещенного списка.

+0

Не работает для списка с нечетным числом элементов. – Renzo

+0

@Renzo Спасибо, я пропустил это – coredump

3
(defun double-every-other (xs) 
    (loop for x in xs 
     for doublep = nil then (not doublep) 
     collect (if doublep (* x 2) x))) 
+0

@Svante: было поздно ... спасибо. –

2

другая версия, без петли на всех:

(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) 
+0

Сладкий, узнал что-то новое о mapcar. –

0

Вы можете использовать loop "на" список итерации примитивной. Это принимает список переменных цикла, которые будут «размазаны» по списку, а последний - хвостом всего оставшегося списка. Условие loop for необходимо избегать умножения nil, если у нас есть нечетное число аргументов.

(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)))) 

И если мы попытаемся запустить его:

* (double-every-other '(1 2 3 4 5)) 

(1 4 3 8 5) 
Смежные вопросы