Macros 使用特殊变量作为宏输入?

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)…),它可以正常工作,但是调用

我想制作一个宏,用于将变量绑定到给定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)…)
,它可以正常工作,但是调用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
技巧来实现它。