我正在尝试在lisp中编写一个宏,它返回传递给它的第n个表达式,并且只计算该表达式。例如:
(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)))))))
答案 0 :(得分:5)
主要是FIRST提出扩展。
工作代码应该是什么样的?宏用法将扩展到的代码?
然后你编写宏来创建这样的代码。
确保您没有评估宏中提供的任何代码。
您的问题的一个简单有用的扩展目标是CASE表单。
(case n
(0 (do-this))
(1 (do-that))
(2 (do-something-else)))
现在编写一个将(nth-expr n (/ 1 0) (+ 1 2) (/ 1 0))
扩展为CASE表格的宏应该很容易......
答案 1 :(得分: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)))
答案 2 :(得分: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)))
答案 3 :(得分:0)
第二个示例中的错误是EVAL
无法看到您的词汇绑定N
,X
和Y
变量。
来自EVAL
的CLHS:
在当前动态环境和 null词法环境中评估表单。
如果您将X
和Y
声明为特殊:
(let ((n 3) (x "win") (y "lose"))
(declare (special x y))
(nth-expr n (princ x) (princ x) (princ y) (princ x)))
但这仍然不如SK-logic建议的CASE
解决方案好。