我以引用的形式存储一些宏(因为实际上它们会产生具有棘手词汇环境的lambda,我更喜欢将它们序列化并将它们序列化为列表)。所以现在我正在尝试:
(defun play (s)
(funcall (macroexpand s)))
Macroexpand评估引用的lambda,因此funcall无法运行它。如何在没有macroexpand
的情况下取消引用eval
的结果?因为在我的情况下,它会导致无法辩护的安全漏洞。
更多信息:
我看起来像这样(最简单的情况):
FUNCALL:#1 =#'(LAMBDA(#:G6008)(SYMBOL-MACROLET NIL T))不是函数名;尝试使用符号
和symbol-macrolet
实际上构建了“棘手的词汇环境”里面 lambda。
答案 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 ...)
列表的函数,则可以使用coerce
和funcall
及其结果。这包括宏扩展,但您可能希望使用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没有提供参数。