2017-05-04 48 views
1

我剛開始學習宏函數的概念。在lisp中創建與incf等效的宏函數

我的老師要求我們創建一個宏函數,其功能與incf完全一樣。

這是他給了我們對流行

(defmacro mypop (nom) 
    (list 'prog1 (list 'car nom) (list 'setq nom (list 'cdr nom)))) 

這裏是我想變成一個宏觀的常規功能的例子:

(defun iincf (elem &optional num) 
    (cond 
     ((not num) (setq elem (+ 1 elem))) 
     (t (setq elem (+ num elem))))) 

這裏是我把它企圖成宏:

(defmacro myincf (elem &optional num) 
    (list 'cond 
     ((list 'not num) (list 'setq elem (list '+ 1 elem))) 
     (t (list 'setq elem (list '+ num elem))))) 

不過,我得到這個錯誤,我不知道爲什麼:

*** - system::%expand-form: (list 'not num) should be a lambda expression 

另外,我不確定我的函數是否真的會改變頂層變量的值。

因此,這裏有我的2個問題:

  1. 爲什麼我得到這個錯誤?
  2. 函數我試圖變成宏罰款? (如果成功將它變成宏功能,它會按照我的意圖去做嗎?)

PS:我知道這個練習可能會違反lisp中的許多常用規則,但這僅僅是爲了練習。謝謝! :)

+0

我只想告訴你關於[Portacle](https://github.com/Shinmera/portacle),一個便攜式和多平臺Common Lisp開發環境(運送Emacs,SBCL,Slime,Quicklips和Git),因爲它是獲取一個好的開發環境的最簡單的方法,我知道我很欣賞找到它:),也關於[lisp-lang.org](http://lisp-lang.org/),一個*吸引人的*和現代網站在CL上,如果你不認爲這可能存在(就像我一樣)! – Ehvince

回答

4

的原因錯誤是你的語法是無效的:

((list ...) ...) 
(t (list ...)) 

的第一要素應該是一個函數名或lambda表達式,所以你需要將其更改爲類似

(list (list ...) ...) 
(list t (list ...)) 

雖然宏還不是一個很好的。首先,backquote語法會使代碼更具可讀性。它允許您在只評估指定表單的情況下編寫模板。例如,給定的MYPOP宏看起來像

(defmacro mypop (nom) 
    `(prog1 (car ,nom) 
    (setq ,nom (cdr ,nom)))) 

只有形式用逗號他們進行評估之前。同樣的,您的宏:

(defmacro myincf (elem &optional num) 
    `(cond 
    ((not ,num) (setq ,elem (+ 1 ,elem))) 
    (t (setq ,elem (+ ,num ,elem))))) 

COND真的不應該是擴展的一部分,雖然。應在宏展開期間對其進行評估,並且只返回其中一個分支的SETQ表單。

(defmacro myincf (elem &optional num) 
    (cond 
    ((not num) `(setq ,elem (+ 1 ,elem))) 
    (t `(setq ,elem (+ ,num ,elem))))) 

的兩個分支之間的唯一差別是,第一個默認爲1NUM。一個簡單的方法可以實現NUM的默認值。

(defmacro myincf (elem &optional (num 1)) 
    `(setq ,elem (+ ,num ,elem))) 

當然,標準INCF是更復雜一點,因爲它適用於各種各樣的地方(而不僅僅是變量),並確保地方的子表單只計算一次。但是,由於MYPOP示例沒有處理這些,所以我不認爲你必須這樣做。

如果你想,一個簡單的方法來定義這樣一個宏將

(define-modify-macro myincf (&optional (num 1)) +) 

或者你可以用一些手工做同樣喜歡

(defmacro myincf (place &optional (num 1) &environment env) 
    (multiple-value-bind (dummies vals store setter getter) 
     (get-setf-expansion place env) 
    `(let* (,@(mapcar #'list dummies vals) 
      (,(first store) (+ ,getter ,num))) 
     ,setter))) 

但使用DEFINE-MODIFY-MACRO將是preferrable一個真正的程序(更短的代碼,更少的錯誤)。如果你有興趣,你可以閱讀GET-SETF-EXPANSIONDEFINE-MODIFY-MACRO

+0

感謝您的詳細和有用的答案。我花了幾天時間研究它並學習相應的語法,但它非常有用。 –