Macros 使用特殊变量作为宏输入?
我想制作一个宏,用于将变量绑定到给定var列表和val列表的值 这是我的密码-Macros 使用特殊变量作为宏输入?,macros,common-lisp,Macros,Common Lisp,我想制作一个宏,用于将变量绑定到给定var列表和val列表的值 这是我的密码- (defmacro let-bind (vars vals &body body) `(let ,(loop for x in vars for y in vals collect `(,x ,y)) ,@body)) 虽然如果调用like(让bind(ab)(12)…),它可以正常工作,但是调用
(defmacro let-bind (vars vals &body body)
`(let ,(loop for x in vars
for y in vals
collect `(,x ,y))
,@body))
虽然如果调用like(让bind(ab)(12)…)
,它可以正常工作,但是调用like时它似乎不工作
(defvar vars '(a b))
(defvar vals '(1 2))
(let-bind vars vals ..)
然后我也看到了我的其他宏的一些效果。我是一名学习者,无法找出错误所在。基本问题:宏看到的是代码,而不是值。函数看到的是值,而不是代码
CL-USER 2 > (defvar *vars* '(a b))
*VARS*
CL-USER 3 > (defvar *vals* '(1 2))
*VALS*
CL-USER 4 > (defmacro let-bind (vars vals &body body)
(format t "~%the value of vars is: ~a~%" vars)
`(let ,(loop for x in vars
for y in vals
collect `(,x ,y))
,@body))
LET-BIND
CL-USER 5 > (let-bind *vars* *vals* t)
the value of vars is: *VARS*
Error: *VARS* (of type SYMBOL) is not of type LIST.
1 (abort) Return to top loop level 0.
您可以看到vars
的值是*vars*
。这是一个象征。因为宏变量绑定到代码片段,而不是它们的值
因此,在宏中,您尝试迭代符号*vars*
。但是*vars*
是一个符号,而不是一个列表
现在可以尝试在宏展开时计算符号*vars*
。但这在一般情况下也不起作用,因为在宏扩展时,*vars*
可能没有值
宏扩展为
let
形式,但let
要求在编译时使用实变量。您不能在以后的时间点计算let
的变量。这只适用于在运行时反复展开宏的某些解释代码。基本问题:宏看到的是代码,而不是值。函数看到的是值,而不是代码
CL-USER 2 > (defvar *vars* '(a b))
*VARS*
CL-USER 3 > (defvar *vals* '(1 2))
*VALS*
CL-USER 4 > (defmacro let-bind (vars vals &body body)
(format t "~%the value of vars is: ~a~%" vars)
`(let ,(loop for x in vars
for y in vals
collect `(,x ,y))
,@body))
LET-BIND
CL-USER 5 > (let-bind *vars* *vals* t)
the value of vars is: *VARS*
Error: *VARS* (of type SYMBOL) is not of type LIST.
1 (abort) Return to top loop level 0.
您可以看到vars
的值是*vars*
。这是一个象征。因为宏变量绑定到代码片段,而不是它们的值
因此,在宏中,您尝试迭代符号*vars*
。但是*vars*
是一个符号,而不是一个列表
现在可以尝试在宏展开时计算符号*vars*
。但这在一般情况下也不起作用,因为在宏扩展时,*vars*
可能没有值
宏扩展为
let
形式,但let
要求在编译时使用实变量。您不能在以后的时间点计算let
的变量。这只适用于某些解释代码,其中宏将在运行时一次又一次地展开。如果您已经阅读了其他答案,那么您就知道无法从compiletime宏读取运行时值(或者更确切地说,您无法知道它在运行时在compiletime的值,因为您看不到未来)。所以,让我们问一个不同的问题:如何在运行时绑定列表中已知的变量
如果您的列表不是真正可变的,您只想给它一个名称,您可以使用macroexpand:
(defun symbol-list-of (x env)
(etypecase x
(list x)
(symbol (macroexpand x env))))
(defmacro let-bind (vars vals &body body &environment env)
(let* ((vars (symbol-list-of vars env))
(syms (loop for () in vars collect gensym)))
`(destructuring-bind ,syms ,vals
(let ,(loop for sym in syms for bar in vars collect (list var sym)) ,@body))))
这会在一定程度上满足您的要求。它将展开第一个参数并计算第二个参数
如果要计算第一个参数,该怎么办?我们可以尝试生成使用eval
的东西。由于eval
将在空词汇环境中求值(即不能引用任何外部局部变量),我们需要让eval生成一个函数来绑定变量,然后调用另一个函数。这是一个类似于(lambda(f)(let(…)(funcall f))
的函数。您可以对表达式求值以获得该函数,然后使用一个不包含主体的函数调用它(但不是由eval
生成的,因此捕获封闭范围)。请注意,这意味着您只能绑定动态变量
如果要绑定词法变量,该怎么办?在Common Lisp中,在运行时无法从符号到变量的内存位置。调试器可能知道如何执行此操作。虽然编译器知道这一点,但无法获取宏范围内的变量列表。因此,无法生成函数来设置词法绑定的符号如果你想隐藏绑定,那就更难了,尽管如果你知道范围中的每个变量,你可以用一些symbol宏来做
但是对于特殊变量,也许有一种更好的方法可以做到这一点,事实证明确实如此。它是一种叫做progv
的模糊特殊形式。它具有与let bind
相同的特征码,只是它可以工作。如果您已经阅读了其他答案,那么您就知道无法从编译器读取运行时值ime宏(或者更确切地说,您无法知道它在compiletime运行时的值,因为您看不到未来)。因此,让我们问一个不同的问题:如何在运行时绑定列表中已知的变量
如果您的列表不是真正可变的,您只想给它一个名称,您可以使用macroexpand:
(defun symbol-list-of (x env)
(etypecase x
(list x)
(symbol (macroexpand x env))))
(defmacro let-bind (vars vals &body body &environment env)
(let* ((vars (symbol-list-of vars env))
(syms (loop for () in vars collect gensym)))
`(destructuring-bind ,syms ,vals
(let ,(loop for sym in syms for bar in vars collect (list var sym)) ,@body))))
这在某种程度上可以满足您的需要,它将扩展第一个参数并计算第二个参数
如果您要计算第一个参数,该怎么办?我们可以尝试生成使用eval
的内容。因为eval
将在空词汇环境中计算(即不能引用任何外部局部变量),我们需要让eval生成一个函数来绑定变量,然后调用另一个函数。这是一个类似于(lambda(f)(let(…)(funcall f))
的函数。您需要对表达式求值以获得该函数,然后使用不影响函数体的函数调用它(但不是由eval
生成的,因此捕获了封闭范围)。请注意,这意味着您只能绑定动态变量
如果要绑定词法变量,该怎么办?在Common Lisp中,在运行时无法从符号到变量的内存位置。调试器可能知道如何执行此操作。虽然编译器知道这一点,但无法获取宏范围内的变量列表。因此,无法生成函数来设置词法绑定的符号如果你想隐藏绑定,那就更难了,尽管如果你知道范围内的每个变量,你可以用一些symbol macrolet
技巧来实现它。