2016-08-19 1 views
2

Jungs.Probleme beim Schreiben `loop ... collect` in Makro

Heute möchte ich Sigma Makro schreiben, um die Summe aus dem flexiblen Ausdruck Eingang zu berechnen.

Der Code unten ist ich heute Nachmittag geschrieben. Aber es funktioniert nicht meinem Zweck folgen.

(defmacro sigma (exp ll) 
    `(+ ,@(loop for i in ll collect 
      (progn (setf (elt exp 1) i) 
        (print exp) 
        exp))) 
) 

>>(pprint (macroexpand-1 '(sigma (+ 1 2) (2 3 4)))) 
>>(+ 2 2) 
    (+ 3 2) 
    (+ 4 2) 
    (+ (+ 4 2) (+ 4 2) (+ 4 2)) 

Ich will es funktioniert (+ (+ 2 2) (+ 3 2) (+ 4 2)) aber loop collect mir die seltsame Antwort geben.

Warum funktioniert das so? Habe ich einige Methoden, um das zu beheben?

Antwort

3

Wenn Sie eine frisch Consed Liste wollen, dann copy-list ein Weg ist

(defmacro sigma ((op arg0 &rest args) ll) 
    (declare (ignore arg0)) 
    `(+ ,@(loop for i in ll collect `(,op ,i ,@args)))) 
6

Sie mutieren literale Daten (notiert). Wenn Sie zustimmen, dass während dieser Schleife die an exp gebundene Liste (+ 1 2) in jeder Iteration dieselbe ist und dass Sie das zweite Element in jeder Iteration mutieren, ist es leicht vorstellbar, dass eine Liste, die dieselbe Liste exp 3 mal gesammelt hat, hätte 3 genau die gleichen Elemente mit der allerletzten Mutation des zweiten Elements.

Dies ist keineswegs eine Funktion in Makros. Mutationen an allen zitierten Daten können zu einem solchen Ergebnis führen. Der Standard schreibt vor, dass das Ergebnis undefiniert ist, sodass kein Implementierer dies ansprechen muss und Sie das unerwartete Verhalten von anderen Aspekten der bestimmten Implementierung erhalten.

Eine kompilierte Datei könnte alle zitierten Daten zusammenfügen, um ein und dasselbe zu werden, so dass '(+ 1 2) andere Stellen im Code ebenfalls von diesem Makro betroffen sein können.

dies zu beheben einfach nicht mutiert nicht:

(defmacro sigma ((op r &rest rest) ll) 
    `(+ ,@(loop :for i :in ll 
       :collect (list* op i rest)))) 

(macroexpand-1 '(sigma (+ 1 2) (2 3 4))) 
; ==> (+ (+ 2 2) (+ 3 2) (+ 4 2)) 

Das Schöne daran ist, dass Sie mindestens zwei Argumente in der Vorlage haben, sind garantiert.

Verschachtelte Ausdrücke Backquote sind auch möglich:

(macroexpand-1 '(sigma (x) (2 3 4))) 
; ==> *** - SIGMA: (X) does not match lambda list element (OP R &REST REST) 
+1

Auch wenn er nicht mutiert waren es, Er sammelt jedes Mal das gleiche 'exp'-Objekt durch die Schleife. – Barmar

+0

@Barmar Ich erinnere mich, ich einmal 'NCONC'-ed das gleiche Argument zweimal, um eine doppelt große Liste zu bekommen :-) – Sylwester