2016-08-19 19 views
2

guys。在宏中編寫`loop ... collect`時出現問題

今天我想寫西格瑪宏來計算從靈活的表達式輸入的總和。

下面的代碼是我今天下午寫的。但它不符合我的目的。

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

我想它的工作原理(+ (+ 2 2) (+ 3 2) (+ 4 2))loop collect給我奇怪的答案。

它爲什麼這樣工作?我有一些方法可以解決這個問題嗎?

回答

3

如果你想要一個新鮮consed列表,然後copy-list一個辦法:

嵌套反引號表達式也是可能的:

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

您正在突變文字數據(帶引號)。如果你同意,在這個循環中,綁定到exp的列表(+ 1 2)在每次迭代中都是相同的,並且你在每次迭代中改變第二個元素,很容易想象3次收集了相同列表exp的列表會有3個與第二個元素的最後一個突變完全相同的元素。

這絕不是宏中的功能。對所有引用數據進行突變可以產生這樣的結果。該標準規定結果將是未定義的,因此沒有實現者需要解決這個問題,並且您會從特定實現的其他方面獲得意想不到的行爲。

編譯後的文件可能會將所有引用的數據合併爲一個,並且代碼中的其他位置也可能受此宏影響。

要根本解決這個問題不發生變異:

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

關於這樣做的好處是,你都保證在模板中至少有兩個參數。

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

即使他沒有變異它,他每次都通過循環收集相同的'exp'對象。 – Barmar

+0

@Barmar我記得我曾經'NCONC'-兩次得到一個雙重大小的列表相同的論點:-) – Sylwester