2012-11-02 55 views
0

我想要的清單傳遞給宏,例如:如何將一個列表傳遞給通用lisp中的宏?

(defmacro print-lst (lst) 
    `(progn 
    ,@(mapcar #'(lambda (x) `(print ,x)) lst))) 
(let ((lst '(1 2 3))) 
     (print-lst lst)) 

它抓住了錯誤:「值是LST LST型不是」。

所以,我的問題是,這段代碼有什麼問題以及如何將列表傳遞給宏?

回答

7

我不知道你爲什麼要把它定義爲一個宏而不是一個常規函數,但問題是宏不會​​評估它們的參數。如果你給它一個詞彙變量的名字,它看到的只是名字('LST),而不是綁定的值。它正在抱怨(正確)符號'LST不是一個列表,因此不是MAPCAR的有效第二個參數。

你可以把它作爲(print-lst (1 2 3)),但你可以在沒有宏觀做,只是做(mapC#'print lst)

3

你試圖用你的宏做的是擴大文字列表。

宏參數未被評估。所以,print-lst實際上是收到符號lst,而不是綁定到變量的列表。

你要麼aknowledge說,給print-lst文字列表,或者您可以生成評估宏參數代碼:

(defmacro print-lst (lst) 
    (let ((item (gensym))) 
    ;; Macros usually make sure that expanded arguments are 
    ;; evaluated only once and in left-to-right order. 
    ;; 
    ;; In this case, we only have one argument and we only evaluate it once. 
    `(dolist (,item ,lst) 
     (print ,item)))) 

雖然,這顯然不是一個宏觀的一個很好的例子,它會更好是一個函數:

(defun print-lst (lst) 
    (dolist (item lst) 
    (print item))) 

如果你想調用內聯到print-lst,您可以諮詢您的實現文檔,看它是否注重(declaim (inline print-lst))

另一種選擇是使用編譯器宏來補充函數,以在編譯時對參數的求值是已知值的內聯調用,但再次查看您的實現是否注意編譯器宏:

(define-compiler-macro print-lst (&whole form lst &environment env) 
    (declare (ignorable env)) 
    ;; Some implementations have an eval function that takes an environment. 
    ;; Since that's not standard Common Lisp, we don't use it in constantp. 
    (if (constantp lst) 
     `(progn 
     ,@(mapcar #'(lambda (item) 
         `(print ,item)) 
        (eval lst))) 
     ;; Return the original form to state you didn't transform code. 
     form)) 
0

您的代碼不必要地使用了宏。其實,你可以像上面提到的那樣評估(mapcar#'print'(1 2 3))。您也可以使用(print-lst(1 2 3))而不使用列表引號,但不推薦使用它,因爲這不符合一般慣例以參數調用函數。總而言之,當調用一個宏時,如果你期望它被評估,你不應該引用一個沒有評估逗號'''的符號,因爲從字面上代入宏模板。

例如

(setq a 1) 

;;A won't be evaluated without the comma 
(defmacro foo (x) `(progn ,(print x)) 
(foo 'a) ;;=> 'A (A is returned since (print 'a) is evaluated) 
(foo a) ;;=> A (1 is returned since (print a) is evaluated) 

;;A is evaluated 
(defmacro bar (x) `(print ,x)) 
(bar 'a) ;;=> A 
(bar a) ;;=> 1 

您的代碼:

(defmacro print-lst (lst)                      
    `(progn                          
    ,@(mapcar #'(lambda (x) `(print ,x)) lst))) 

的「mapcar」的形式將被評估作爲一個整體,但「樂善堂」將只被替換爲你傳遞什麼。

我可以定義的,而不是一個宏功能來解決這個問題:

(defun print-lst (lst) 
    (mapcar #'eval 
    (mapcar #'(lambda (x) `(print ,x)) lst))) 

然後:

(let ((lst '(1 2 3))) 
    (print-lst lst)) 

;;=> 
1 
2 
3 
(1 2 3) 

如果你有興趣的問題,你標題,你可以看到我的另一回答關於此:

How do I apply "or" to a list in elisp