2016-08-07 60 views
3

Ich möchte die Funktion (* x 2) auf jedes andere Element in einer Liste anwenden und die gesamte Liste mit dem Makro loop zurückgeben. Die Lösung, die ich mit so weit habe kommen, ist dies:Common Lisp - Funktion auf jedes andere Element in der Liste anwenden

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

Dies wird jedoch doppelt jedes andere Element und nur die Elemente zurück, die verdoppelt wurden, also wenn ich ausgeführt:

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

Die Ergebnis wäre:

'(4 8) 

Aber ich will das Ergebnis sein:

'(1 4 3 8) 

Gibt es eine Möglichkeit, dies mit (Schleife) zu tun?

Antwort

5

Sie können zum Beispiel Erhöhung eine ganze Zahl testen, während die Liste durchsucht:

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

Eine andere Version mit weniger Mathematik:

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

Offensichtlich werden Sie nicht zu abstrahieren die N Lage sein so leicht wie die andere Antwort (wenn Sie "Makro" denken, hören Sie jetzt auf). Hier durchlaufen wir das Schlüsselwort on, was bedeutet, dass jede Unterliste der Reihe nach besucht wird. Da wir by #'cddr verwenden, wird jede andere Unterliste übersprungen. Die Destrukturierungssyntax (a b) bindet das erste und zweite Element der besuchten Liste.

+0

Hat mit einer ungeraden Anzahl von Elementen für eine Liste nicht. – Renzo

+0

@Renzo Danke, ich habe es verpasst – 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: es war spät ... danke. –

2

eine andere Version, ohne Schleife überhaupt:

(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

Süße, lernte etwas neues über mapcar. –

0

Sie die loop primitive "auf" Liste Iteration verwenden könnte. Dies nimmt eine Liste von Schleifenvariablen auf, die über die Liste "verschmiert" werden, wobei die letzte der Rest der gesamten verbleibenden Liste ist. Die Bedingung loop für ist notwendig, um zu vermeiden, nil zu multiplizieren, wenn wir eine ungerade Anzahl von Argumenten haben.

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

Und wenn wir versuchen, es zu laufen:

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

(1 4 3 8 5)