列出不在宏内部求值的 gensym 符号
List gensym symbol not evaluating inside macro
我正在尝试编写一个宏,它接受变量列表和代码主体,并确保在执行代码主体后变量恢复到它们的原始值(Paul Graham 中的练习 10.6 ANSI Common Lisp).
但是,我不清楚为什么我的 gensym
在一个地方的评估结果与我预期的一样,但在另一个类似的地方却没有(注意:我知道有一个更好的练习解决方案。我只是想弄清楚为什么评价不同)。
这是第一个定义,其中 lst
gensym 求值为 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))))
但是,虽然它的工作方式与我预期的完全一样,但它并不是练习的正确解决方案,因为我在尝试重置值时总是抓住 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))))
但现在我收到一条错误消息,提示 #:G3984
不是列表。如果我用 (symbol-value lst)
替换它,我会收到一条错误消息,指出变量没有值。但为什么不呢?为什么它在 lambda
中的 setf
中有一个值,而不是作为传递给 mapcar
的参数?
在宏展开时,您尝试映射 lst
的值,这是当时的一个符号。所以这没有意义。
尝试获取符号值也毫无意义,因为 lst
的绑定是词法的,而 symbol-value
不是访问它的方法。其他绑定暂时不可用。
显然 lst
在宏展开时有一个值:一个符号。这就是您在 lambda 中看到的内容。
您需要弄清楚哪些值是在宏展开时计算的,哪些是在运行时计算的。
关于命名的建议:
lst
在 Lisp 中是一个糟糕的名字,使用 list
- 名称
lst
没有意义,因为它的值不是一个列表,而是一个符号。我称之为 list-variable-symbol
。看起来很长,不是吗?但它更清楚。你现在会认为它是一个符号,用作变量持有列表的名称。
我敢肯定你想多了。想象一下:
(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
进行阴影绑定,除非我误解了赋值。
你过于复杂的宏会做很糟糕的事情。就像获取全局符号编译时间的值一样,这会将它们重置为使用它而不是主体之前的函数的时间。
我正在尝试编写一个宏,它接受变量列表和代码主体,并确保在执行代码主体后变量恢复到它们的原始值(Paul Graham 中的练习 10.6 ANSI Common Lisp).
但是,我不清楚为什么我的 gensym
在一个地方的评估结果与我预期的一样,但在另一个类似的地方却没有(注意:我知道有一个更好的练习解决方案。我只是想弄清楚为什么评价不同)。
这是第一个定义,其中 lst
gensym 求值为 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))))
但是,虽然它的工作方式与我预期的完全一样,但它并不是练习的正确解决方案,因为我在尝试重置值时总是抓住 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))))
但现在我收到一条错误消息,提示 #:G3984
不是列表。如果我用 (symbol-value lst)
替换它,我会收到一条错误消息,指出变量没有值。但为什么不呢?为什么它在 lambda
中的 setf
中有一个值,而不是作为传递给 mapcar
的参数?
在宏展开时,您尝试映射 lst
的值,这是当时的一个符号。所以这没有意义。
尝试获取符号值也毫无意义,因为 lst
的绑定是词法的,而 symbol-value
不是访问它的方法。其他绑定暂时不可用。
显然 lst
在宏展开时有一个值:一个符号。这就是您在 lambda 中看到的内容。
您需要弄清楚哪些值是在宏展开时计算的,哪些是在运行时计算的。
关于命名的建议:
lst
在 Lisp 中是一个糟糕的名字,使用list
- 名称
lst
没有意义,因为它的值不是一个列表,而是一个符号。我称之为list-variable-symbol
。看起来很长,不是吗?但它更清楚。你现在会认为它是一个符号,用作变量持有列表的名称。
我敢肯定你想多了。想象一下:
(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
进行阴影绑定,除非我误解了赋值。
你过于复杂的宏会做很糟糕的事情。就像获取全局符号编译时间的值一样,这会将它们重置为使用它而不是主体之前的函数的时间。