2011-11-30 117 views
5

我想在lisp中編寫一個宏,它返回傳遞給它的第n個表達式,並且只評估該表達式。例如:Lisp宏評估表達式,當我不想要它

(let ((n 2)) 
    (nth-expr n (/ 1 0) (+ 1 2) (/ 1 0))) 

應該返回3.我得到一個除以0的錯誤。我的宏定義如下:

(defmacro nth-expr (n &rest expressions) 
    (let ((i (gensym)) 
     (expr (gensym))) 
    `(do ((,i 1 (1+ ,i)) 
      (,expr ,expressions (cdr ,expr))) 
     ((= ,i ,n) (car ,expr))))) 

任何想法我做錯了什麼?謝謝。

編輯:

感謝@Vsevolod Dyomkin幫我與上面​​的部分。現在還有一個問題。當我嘗試做

(let ((n 3) (x "win") (y "lose")) 
    (nth-expr n (princ x) (princ x) (princ y) (princ x))) 

我得到錯誤Error: Attempt to take the value of the unbound variable 'Y'

我更新的代碼看起來是這樣的:

(defmacro nth-expr (n &rest expressions) 
    (let ((i (gensym)) 
     (expr (gensym)) 
     (num (gensym))) 
    `(let ((,num (eval ,n))) 
     (do ((,i 1 (1+ ,i)) 
      (,expr ',expressions (cdr ,expr))) 
      ((= ,i ,num) (eval (car ,expr))))))) 
+1

你爲什麼要在宏中調用'eval'? – leppie

+0

@leppie,因爲如果我不這樣做,它會返回(princ y),但我希望對它進行評估。 – Daniel

+1

'Y'在該階段不存在。你想做什麼? – leppie

回答

1

你得qoute,expressions,像這樣:

(defmacro nth-expr (n &rest expressions) 
    (let ((i (gensym)) 
     (expr (gensym))) 
    `(do ((,i 1 (1+ ,i)) 
      (,expr ',expressions (cdr ,expr))) 
     ((= ,i ,n) (car ,expr))))) 

否則,你會得到是這樣的 - 在expressions名單按原樣插入:

CL-USER> (let ((n 2)) 
      (macroexpand-1 '(nth-expr n (/ 1 0) (+ 1 2) (/ 1 0)))) 
(DO ((#:G864 1 (1+ #:G864)) 
    (#:G865 ((/ 1 0) (+ 1 2) (/ 1 0)) (CDR #:G865))) 
    ((= #:G864 N) (CAR #:G865))) 
+0

啊,這是有幫助的。那麼我想我必須拋出一個'eval'來實際評估'(+ 1 2)',而不是僅僅返回''(+ 1 2)' – Daniel

+0

我已經問過上面的一個跟進問題。你認爲你可以嘗試幫助嗎?謝謝。 – Daniel

+0

我會說,實現你的目標,即只評估第n個表達式的最簡單的方法就是:'(defmacro nth-expr(n&body expressions)(n n n expressions))' –

5

最主要的是要先來隨着擴張。

工作代碼應該是什麼樣子?宏使用將擴展到的代碼?

然後你編寫宏來創建這樣的代碼。

確保您不要評估宏中提供的任何代碼。

您的問題的一個簡單有用的擴展目標是CASE表單。

(case n 
    (0 (do-this)) 
    (1 (do-that)) 
    (2 (do-something-else))) 

現在,應該是很容易寫一個擴展(nth-expr n (/ 1 0) (+ 1 2) (/ 1 0))成箱形狀宏...

4

你不需要eval在這裏,並在一個列表存儲表達式沒有必要。

一個適當的實施以下內容:

(defmacro nth-expr (n &rest expressions) 
    `(case ,n 
     ,@(loop for e in expressions 
       for n from 1 
       collect 
       `((,n) ,e)))) 

你的榜樣擴展爲:

(CASE N ((1) (/ 1 0)) ((2) (+ 1 2)) ((3) (/ 1 0))) 
0

在你的第二個例子中的錯誤是EVAL不能看到你的詞彙綁定NXY變量。

從CLHS爲EVAL

求值形式在當前動態環境空詞法環境

,如果你宣佈XY特殊它的工作:

(let ((n 3) (x "win") (y "lose")) 
    (declare (special x y)) 
    (nth-expr n (princ x) (princ x) (princ y) (princ x))) 

但這仍然不如CASE溶液SK-logic建議爲好。