遞歸宏是不可能的
想象一下,你嘗試過:
(defmacro expand (&rest elements)
(if (not (null (cdr elements)))
`(+ ,(car elements) ,(expand (cdr elements)))
(car elements)))
現在,當宏功能正在編譯它擴展所有宏,以便它調用它(expand (cdr elements))
..
(defmacro expand (&rest elements)
(if (not (null (cdr elements)))
`(+ ,(car elements) (+ (cdr elements) (+ (cdr elements) (+ (cdr elements) (+ (cdr elements) ...))))
(car elements)))
你看到了嗎?現在想象你,而不是僅僅擴大了第一部分,而不是遞歸,但留下一個更簡單的表達,而不是expand
:
(defmacro expand (&rest elements)
(if (not (null (cdr elements)))
`(+ ,(car elements) (expand ,@(cdr elements)))
(car elements)))
這是完全不同的,因爲宏從不直接使用宏。然而(expand 1 2 3)
擴展到(+ 1 (expand 2 3))
和Lisp繼續,直到有沒有向左擴大宏,留下(+ 1 (+ 2 3))
沒有遞歸
遞歸函數都ok的宏:
(defmacro expand (&rest elements)
(labels ((recfun (elements)
(if (not (null (cdr elements)))
`(+ ,(car elements) ,(recfun (cdr elements)))
(car elements))))
(recfun elements)))
它並不需要是一個本地定義的功能。通常我實現大多數的功能函數,使宏延遲的一些參數評測離開宏只是調用函數:
(defun make-env-fun (names)
(mapcar (lambda (name) (cons name (symbol-function name))) names))
(defmacro make-env (&rest variables)
`(make-env-fun ',variables))
(make-env cons car cdr)
; ==> ((cons . #<system-function cons>)
; (car . #<system-function car>)
; (cdr . #<system-function cdr>))
所以宏存在,因爲我不想做(make-env 'cons 'car 'cdr)
或(make-env '(cons car cdr))
。宏只解決了這個問題,而不是功能仍在做的實際工作。
因此,爲了與您的問題相關,您需要一個允許(already-exist symbol ((symbol "bla")))
而不是(already-exist-fun 'symbol '((symbol "bla")))
的宏。你看到了嗎?
你應該爲此使用一個函數。宏用於生成代碼。您也可以使用'ASSOC'(或'(member ...:key#'first)')來查看密鑰是否已經存在。 – jkiiski
@jkiiski我需要將一個未加引號的符號傳遞給宏,我不希望代碼評估該符號,所以出於這個原因,我選擇編寫一個宏而不是函數。有什麼方法可以在函數中獲得相同的行爲?如果你知道,請告訴我。無論如何,我不知道ASSOC,所以謝謝!我會記住的! – Jim
您應該只引用符號或使用關鍵字。搞亂看起來像一個函數的表單的評估會讓人們閱讀代碼時感到困惑。 – jkiiski