2010-10-06 66 views
7

我有一個類中的Common Lisp:defmacro與defclass

(defclass my-cool-class() 
    ((variable1 
    :initarg :variable1 
    :accessor variable1 
    :initform (error "Must supply value to variable1")) 
    (variable2 
    :initarg :variable2 
    :accessor variable2 
    :initform (error "Must supply value to variable2")) 

我想創建一個可以簡化打字

(defmacro make-slot (slot-name) 
    `(slot-name 
    :initarg :,slot-name 
    :accessor :,slot-name 
    :initform (error "Must supply value"))) 

的這種冗餘宏最後,我想有(defclass my-cool-class()(make-slots'(foo bar baz)),並獲得foo,bar和baz作爲插槽自動。

但是,當我去做一個macroexpand-1 make-嗨,我得到讀者的錯誤。

第一個是「冒號後非法終止字符」,然後繼續執行。

SBCL 1.0.37。

編輯:這些示例在系統上是語法正確的,我在複製之前做了一些修改。


半年後 -

(defun build-var (classname var) 
    (list var 
     :initform nil 
     :accessor (intern (concatenate 'string (string classname) "-" 
             (string var))) 
     :initarg (intern (string var) :keyword))) 

(defun build-varlist (classname varlist) 
    (loop for var in varlist 
     collect (build-var classname var))) 


(defmacro defobject (name &rest varlist) 
    "Defines a class with a set of behavior. 
    Variables are accessed by name-varname. 

    (defobject classname v1 v2 v3) 
    " 
    `(defclass ,name() 
    ,(build-varlist name varlist))): 

兩個半年以後。

我在其他地方發現了六個月大的代碼。雖然我受寵若驚,但它也提醒我要更新這一點。

如果你喜歡這個想法,我保留這個代碼在:https://github.com/pnathan/defobject。與以前一樣,其目標是以最少的重複鍵入來生成CLOS類。一個類似的系統叫做DEFCLASS-STAR。建議有意者對兩者進行審覈。

回答

5

你不能把宏放在你想要的代碼中。閱讀CLHS中構造的語法。

例如,你不能這樣做:

(defun foo (make-arg-list 'a 'b) a b) 

defun定義期望一個arglist中,而不是創建一個arglist中的功能。

Lisp擴展宏,其中Lisp表單是預期的。在預期其他列表(例如插槽列表)的地方,Lisp不會進行宏展開。

類似DEFCLASS需要插槽列表,而不是創建插槽列表的函數。 與插槽列表類似,DEFCLASS希望每個插槽都是描述插槽的名稱或列表。

見DEFCLASS的語法: http://www.lispworks.com/documentation/HyperSpec/Body/m_defcla.htm

你不能也把你想要逗號。

一個基本的Lisp書可能會有所幫助。閱讀關於Lisp語法的內容。

:,foo 

以上是沒有意義的。

逗號運算符將項目放入反向列表中。它不會將項目放入符號中。

如果您想創建一個符號,您需要調用INTERN或MAKE-SYMBOL。

解決方案

寫MY-DEFCLASS宏,允許較短的語法和擴展到DEFCLASS。在庫中已經有了類似這樣的DEFCLASS *宏。

+0

嗯。我認爲 - 錯誤地顯然 - 宏觀將以替代方式擴張;因此,defclass會看到插槽列表。 – 2010-10-06 01:38:29

+0

@Paul Nathan,Lisp應該如何確定MAKE-SLOT是一個宏而不是插槽的名稱? DEFCLASS語法清晰,它需要一個插槽列表。它並不期望一個函數評估爲插槽列表。 – 2010-10-06 01:49:58

+0

啊,我想我的主要錯誤在於我如何擴展宏以及何時。 – 2010-10-06 16:42:27

2

正如Rainer所說,只有在函數調用可以接受的情況下才會擴展宏。

我在限定插槽時需要實際鍵入的限制鍋爐板的方法是使用兩個編輯器宏,一個用於帶讀卡器的插槽,另一個用於帶插槽的插槽(我很少有插槽分開的作家,但如果我做了,我必須親手寫作)。

3

我通常在使用這樣的

(defmacro mydefclass (name fields) 
    `(defclass ,name() 
    ,(let ((res nil)) 
     (dolist (f fields) 
      (let* ((fname (symbol-name f)) 
       (kw (intern fname :keyword))) 
      (push `(,f :accessor ,kw 
         :initarg ,kw 
         :initform (error 
            ,(format NIL "Must supply value to ~a" 
              fname))) 
        res))) 
     (nreverse res)))) 

然後

(mydefclass foo (x y z)) 

添加一些邏輯來處理需要定製插槽是很容易的(例如,你可以拷貝輸入逐字當字段是列表而不是符號時擴展)

0

宏從上到下遞歸地擴展。在您的示例中,首先擴展了defclass宏 - 在您的make-slot宏之前。擴展defclass的代碼不期望未擴展的make-slot宏 - 它期望插槽定義。

正如其他人所建議的,讀者錯誤是因爲`:,symbol是無效的Lisp。但首先將關鍵字傳遞給宏是很容易的。

+1

有趣。我不知道defclass本身就是一個宏。我想我會採取Rainer的建議,並得到一本Lisp書。 – 2010-10-07 05:09:09

+1

@Paul Nathan,所有的DEFSOMETHING都是宏。 – 2010-10-07 13:09:12

相關問題