列出gensym符号不评估内部宏

Uni*_*One 2 lisp macros common-lisp

我正在尝试编写一个宏,它接受一系列变量和一组代码,并确保在执行代码体之后变量恢复为原始值(在Paul Graham的ANSI Common Lisp中练习10.6 ).

但是,我不清楚为什么我的gensym预期会在一个地方进行评估,而不是另一个类似的评估(注意:我知道这个练习有更好的解决方案.我只是想弄清楚为什么评价的差异).

这是第一个定义,其中lstgensym计算到lambda传递给下面的列表mapcar:

(defmacro exec-reset-vars-1 (vars body)
  (let ((lst (gensym)))
    `(let ((,lst ,(reduce #'(lambda (acc var) `(cons ,(symbol-value var) ,acc))
                          vars
                          :initial-value nil)))
         ,@body
         ,@(mapcar #'(lambda (var) `(setf ,var (car ,lst)))
                   vars))))
Run Code Online (Sandbox Code Playgroud)

虽然它完全按照我的预期工作,但它并不是一个正确的解决方案,因为我总是抓住lst尝试重置值时的第一个元素.我真的想映射2个列表.所以现在我写道:

(defmacro exec-reset-vars-2 (vars body)
  (let ((lst (gensym)))
    `(let ((,lst ,(reduce #'(lambda (acc var) `(cons ,(symbol-value var) ,acc))
                          vars
                          :initial-value nil)))
         ,@body
         ,@(mapcar #'(lambda (var val) `(setf ,var ,val))
                   vars
                   lst))))
Run Code Online (Sandbox Code Playgroud)

但现在我得到一个错误,说#:G3984不是列表.如果我替换它(symbol-value lst)我得到一个错误,说变量没有值.但为什么不呢?为什么它在setfin中有一个值lambda,而不是作为参数传递给mapcar

Rai*_*wig 6

在宏展开时,您尝试映射的值lst,即当时的符号.所以这没有意义.

试图获取符号值也没有意义,因为lst绑定是词法,symbol-value并不是访问它的方法.当时没有其他绑定.

显然lst在宏观扩展时有一个值:一个符号.这就是你在lambda中看到的内容.

您需要明确在宏展开时和运行时计算的值.

关于命名的建议:

  • lst 在Lisp中是一个糟糕的名字,使用 list
  • 这个名字lst毫无意义,因为它的值不是列表,而是符号.我叫它list-variable-symbol.看起来很长,不是吗?但它更清楚.您现在将它作为符号,用作变量保持列表的名称.