列出gensym符号不评估内部宏

时间:2016-06-25 17:36:32

标签: macros lisp common-lisp

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

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

以下是lst gensym评估为传递给lambda的{​​{1}}内部列表的第一个定义:

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)))) 的第一个元素。我真的想映射2个列表。所以现在我写道:

lst

但现在我收到一条错误消息,指出(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)))) 不是列表。如果我用#:G3984替换它,我得到一个错误,说变量没有值。但为什么不呢?为什么它在(symbol-value lst)中的setf内有值,而不是作为传递给lambda的参数?

2 个答案:

答案 0 :(得分:6)

在宏展开时,您尝试映射lst的值,这是当时的符号。所以这毫无意义。

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

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

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

关于命名的建议:

  • lst在Lisp中名称不佳,使用list
  • 名称lst毫无意义,因为它的值不是列表,而是符号。我称之为list-variable-symbol。看起来很长,不是吗?但它更清楚。你现在想它是一个符号,用作变量持有列表的名称。

答案 1 :(得分:3)

我很确定你是在过度思考它。想象一下:

(defparameter *global* 5)
(let ((local 10))
  (with-reset-vars (local *global*)
    (setf *global* 20)
    (setf local 30)
    ...))

我认为扩展很简单:

(defparameter *global* 5)
(let ((local 10))
  (let ((*global* *global*) (local local))
    (setf *global* 20)
    (setf local 30)
    ...)
  (print local)) ; prints 10
(print *global*)  ; prints 5 

let重置它自己,所以你看到宏应该非常简单,只需要与let进行阴影绑定,除非我误解了赋值。

你过于复杂的宏做得非常糟糕。就像获取全局符号编译时的值一样,这会将它们重置为使用它而不是在主体之前的函数。