Macros 当我不希望Lisp宏计算表达式时对其进行求值

Macros 当我不希望Lisp宏计算表达式时对其进行求值,macros,lisp,Macros,Lisp,我试图用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)) (

我试图用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)))
我收到错误
错误:尝试获取未绑定变量“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)))))))
(defmacro nth-expr (n &rest expressions)
  (let ((i (gensym))
        (expr (gensym)))
    `(do ((,i 1 (1+ ,i))
          (,expr ',expressions (cdr ,expr)))
         ((= ,i ,n) (car ,expr)))))

您必须使用以下表达式执行操作:

(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)))))))
(defmacro nth-expr (n &rest expressions)
  (let ((i (gensym))
        (expr (gensym)))
    `(do ((,i 1 (1+ ,i))
          (,expr ',expressions (cdr ,expr)))
         ((= ,i ,n) (car ,expr)))))
否则,您将得到以下结果-
表达式列表按原样插入:

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)))

主要的事情是首先提出扩展

工作代码应该是什么样子?宏用法将扩展到的代码

然后编写宏来创建这样的代码

确保不计算宏中提供的任何代码

对于您的问题,一个简单有用的扩展目标是案例表单

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

现在编写一个宏将
(n表达式n(/10)(+12)(/10))
扩展为大小写形式应该很容易…

这里不需要
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)))

第二个示例中的错误是
EVAL
无法查看词汇绑定的
N
X
Y
变量

评估的CLHS中:

在当前动态环境和空词汇环境中计算表单

如果您将
X
Y
声明为“特殊”,则它将起作用:

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

但这仍然不如所建议的
案例
解决方案好。

Ah这很有帮助。然后我想我必须加入一个
eval
,以便实际评估
(+12)
,而不是仅仅返回
”(+12)
,我在上面问了一个后续问题。你觉得你能帮上忙吗?谢谢。我想说的是,实现目标的最简单方法,即只计算第n个表达式,是这样的:
(defmacro nth expr(n&body expressions)(nth n expressions))
为什么要在宏中调用
eval
?@leppie,因为如果我不这样做,它会返回(prince y)但我希望对其进行评估。
Y
在该阶段不存在。你想干什么?