簡體   English   中英

在通用lisp函數中包含通用lisp宏

[英]Including a common lisp macro in a common lisp function

我是Lisp通用語言的新手。 我才三個月。 有一天,我想到由於是防止引用s表達式的引號,也許我可以通過“去除”該“引號”來編寫自己的評估。 鑒於宏並不等同於它的輸入,我這樣寫:

(defmacro my-eval (x)
             (car (cdr x)))

但這不起作用! 例如,當我編寫(my-eval '(car '(1 2 3))我相信此宏接收(quote (car (quote 1 2 3)))作為其輸入。

但是它把它變成(car (quote (1 2 3)) ,然后求值,它的值是1,但實際上是sbcl1.1.15 print (quote (1 2 3)) 。你能告訴我發生了什么嗎?

好的,所以我看不到宏的任何明顯錯誤,但其名稱除外。 確實,您在報價,不是在逃避。 您不在此處執行代碼(很好),您只是刪除了第一引號。

我可能建議您使用宏的這種細微變化

(defmacro unquote (form)
  (if (eq (first form) 'quote)
      (second form)
      (error "Cannot unquote this form as it is not quoted: ~s" form)))

只是為了增加一點安全性:)

現在回到預期的結果。 鑒於:

(my-eval '(car '(1 2 3))

如您所知,實際上

(my-eval (quote (car (quote (1 2 3)))))

您正在使用(quote (car (quote (1 2 3))))cdr

((car (quote (1 2 3))))

然后,您正在獲取那個的cdr (這是正確的),它給了您

(car (quote (1 2 3)))

到現在為止還挺好。 現在,由於您正在使用宏,因此宏的結果將重新插入代碼中,然后在運行時將執行代碼,從而為您提供正確的結果1

我建議您描述的最后一個問題(使用sbcl 1.1.15)實際上可能是用戶錯誤,因為我已經使用較舊的版本對其進行了測試,並且工作正常。 另外,如果有一個錯誤,那是一種可以在人們的代碼中很快顯示出來的錯誤:)

因此,TLDR宏沒有任何問題,盡管它可以進行一些額外的安全檢查和更佳的名稱。 要記住的主要事情是,宏在宏擴展時正在評估,但它們並未評估其參數(除非您強迫它們執行)。 宏僅返回將替換宏形式並在運行時進行評估的代碼。

祝您好運!

您如何獲得(quote (1 2 3))在SBCL中是一個謎。

是的,如果參數確實是'(car '(1 2 3)) ,則可以刪除引號,但如果您的參數是包含相同的帶引號的形式的變量,則不能刪除引號。 例如。

(my-eval '(car '(1 2 3)))   ; ==> 1
(setf test '(car '(1 2 3))) ; ==> (car '(1 2 3))
(my-eval test)              ; ==> FAIL! 

之所以不起作用,是因為my-eval將接受符號test作為參數,而不是符號后面的值。 (cadr x)就像做(cadr 'test) ,它將失敗。 宏在變量具有值之前(在宏擴展時間內)運行,因此它們可以在您有很多樣板但不能替代eval下幫助減小代碼大小。

您的代碼中發生了什么?

首先,讓我們定義宏,查看宏擴展,並查看使用此宏評估表單的結果。 然后,我們弄清楚為什么每種形式都會產生其作用。 定義很簡單; 您已經提供了:

CL-USER> (defmacro my-eval (x)
           (car (cdr x)))
MY-EVAL

現在讓我們看一下(my-eval '(car '(1 2 3)))的宏擴展是什么:

CL-USER> (macroexpand-1 '(my-eval '(car '(1 2 3))))
(CAR '(1 2 3))
T

這是我們應該期望的。 形式'(car '(1 2 3))的縮寫

(quote (car (quote (1 2 3))))

cdr

((car (quote (1 2 3))))

car

(car (quote (1 2 3)))

那就是(my-eval '(car '(1 2 3)))將被替換的代碼。 這意味着當我們評估(my-eval '(car '(1 2 3))) ,我們應該期望看到與直接評估相同的結果。 當然, (car '(1 2 3))計算結果為1 讓我們檢查:

CL-USER> (my-eval '(car '(1 2 3)))
1

你想做什么?

quote是Common Lisp中的特殊運算符。 (quote object)返回object具有特殊的行為。 object沒有得到評估。 如果是列表,那么您會得到一個列表。 如果是數字,則返回一個數字,依此類推。但是對象從何而來? 在大多數情況下,您是從文件或流中讀取Lisp代碼,這是讀者根據文本表示形式創建對象的責任。 例如,當您輸入'53(quote 53) ,您會得到一個數字,因為閱讀器已經從您那里產生了數字53,並返回了(list 'quote 53) (或(list 'quote '53) )。 當評估者收到該表格時,它會識別出這是一個列表,並且該列表car是符號quote ,因此調用帶有quote的特殊運算符,並使用參數作為列表的第二個對象。 這是編碼到系統中的特殊行為。

另一方面,通過宏,您可以將某些形式(在讀者閱讀之后)轉換為可以傳遞給評估者的新形式。 如果您要嘗試將自己的quote形式的表單實現為宏,那該怎么辦? 它需要轉

(kwote object)

成將保證評估的形式 object 您不能簡單地擴展到object ,因為您不想將對象傳遞給評估者。 如果可以將其視為另一種形式,則評估人員將嘗試對其進行更多評估。 您可以傳遞給評估者以確保可以取回object的唯一方法是列表

(quote object)

這意味着,如果要實現自己的報價,則必須使用內置報價的術語。 例如,

CL-USER> (defmacro kwote (object)
           (list 'quote object))
KWOTE
CL-USER> (kwote (1 2 3))
(1 2 3)
CL-USER> (kwote (car '(1 2 3)))
(CAR '(1 2 3))
CL-USER> (kwote '(car '(1 2 3)))
'(CAR '(1 2 3))

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM