没有评估的不引用

时间:2014-06-01 14:27:39

标签: macros lisp common-lisp eval

我以引用的形式存储一些宏(因为实际上它们会产生具有棘手词汇环境的lambda,我更喜欢将它们序列化并将它们序列化为列表)。所以现在我正在尝试:

(defun play (s)
  (funcall (macroexpand s)))

Macroexpand评估引用的lambda,因此funcall无法运行它。如何在没有macroexpand的情况下取消引用eval的结果?因为在我的情况下,它会导致无法辩护的安全漏洞。

更多信息:

我看起来像这样(最简单的情况):

  

FUNCALL:#1 =#'(LAMBDA(#:G6008)(SYMBOL-MACROLET NIL T))不是函数名;尝试使用符号

symbol-macrolet实际上构建了“棘手的词汇环境”里面 lambda。

3 个答案:

答案 0 :(得分:4)

  

Macroexpand评估引用的lambda,因此funcall无法运行它。如何   没有eval的宏扩展的非引用结果?因为在我的情况下   会造成不可原谅的安全漏洞。

我认为Sylwester关于XY problem的评论可能就在这里;听起来你正试图做一些可能以不同方式做得更好的事情。也就是说,如果你有一个lambda表达式的列表,你可以使用coerce来获取一个函数对象,而不是使用eval。也就是说,你可以这样做:

CL-USER> (funcall '(lambda () 42))
; Error, like you've been having

CL-USER> (funcall (coerce '(lambda () 42) 'function))
42 ; turned the list (lambda () 42) into a function and called it

coerce的文档中对此进行了描述;当“输出”类型为function时,这就是对象参数:

  

如果result-type是function,而object是任何函数名   是fbound但是全局定义既不是宏名也不是   一个特殊的运算符,结果就是对象的功能值。

     

如果result-type是function,而object是lambda表达式,   然后结果是null词法中的对象闭包   环境。

因此,如果您有一个返回表单(lambda ...)列表的函数,则可以使用coercefuncall及其结果。这包括宏扩展,但您可能希望使用macroexpand-1而不是macroexpand,因为lambda已经是一个宏,所以如果扩展得太远,(lambda () ...)会变成{(function (lambda () ...)) 1}}。

CL-USER> (defmacro my-constantly (value)
           `(lambda () ,value))
MY-CONSTANTLY
CL-USER> (macroexpand-1 '(my-constantly 36))
(LAMBDA () 36)
T
CL-USER> (funcall (coerce (macroexpand-1 '(my-constantly 36)) 'function))
36

如果您使用普通macroexpand尝试,那么就会出现问题。考虑一下自己警告:

CL-USER> (macroexpand '(my-constantly 36))
#'(LAMBDA () 36) ; not a list, but a function
T
CL-USER> (funcall (coerce (macroexpand '(my-constantly 36)) 'function))
; Error. :(

答案 1 :(得分:0)

这有几个问题。首先我认为这是一个XY problem,所以如果你展示了更多你的问题,我想我们可能会找到一个解决方案。

它没有任何与unquoting无关,因为在评估引用的表达式时它不再被引用,但它变成了原始引用表达式的数据表示。如果要运行数据,则必须评估数据。

使用eval,您将无法获得当前的词法范围。所以,既然你提到eval并且同一篇文章中的词汇环境让我感觉不到你想要的东西。

现在制作一个功能列表没问题,如下所示:

(list (lambda (x) (+ x x))

由于我使用list而未引用,因此将lambda评估为闭包。如果它已分配给变量x,您可以使用(funcall (car x) 10)) ; ==> 20

调用它

修改 我实际上用这段代码做了同样的错误信息:

(defmacro test () '#'(lambda (x) x))
(macroexpand '(test))             ; ==> #'(lambda (x) x) ; t
(funcall (macroexpand '(test)) 5) ; ==>
*** - funcall: #'(lambda (x) x) is not a function name; try using a symbol
      instead

它不起作用,因为你无法调用lambda未评估。你需要调用闭包(函数),这是lambda形式的评估结果。如果您不引用它,您将在宏中进行评估:

(defmacro test () #'(lambda (x) x))
(macroexpand '(test))             ; ==> #<function :lambda (x) x> ;
(funcall (macroexpand '(test)) 5) ; ==> 5

实际上我不明白为什么你需要把它变成一个宏。让它成为一个功能。

(defun test () #'(lambda (x) x))
(funcall (test) 5 ) ; ==> 5

即使在大多数情况下这更加复杂,你也可以使用闭包。

答案 2 :(得分:0)

我认为这是一个你会发现REPL是你的朋友的情况。为了帮助您入门:

cl-user> (defmacro foo () #'(lambda () :hi))
foo
cl-user> (foo)
#<Compiled-function (:internal foo) (Non-Global)  #x3020014F82FF>
cl-user> (funcall *)
:hi
cl-user> (macroexpand '(foo))
#<Compiled-function (:internal foo) (Non-Global)  #x3020014F82FF>
t
cl-user> (funcall *)
:hi
cl-user> 

我会顺便注意到你示例中出现的lambda表单是一个参数,而你的funcall没有提供参数。