我希望能够从键盘读取lambda表达式。例如,如果功能方块已经被拒绝,我可以输入符号名称:
(defun square (x) (* x x))
以便在评估以下内容时:
(funcall (read) 2)
用户可以键入square
,结果为4。但是,如果用户键入
(lambda (x) (* x x))
结果为错误,例如在Macintosh Common Lisp中,
错误:(LAMBDA(X)(* X X))不能被启用或应用
有没有一种简单的方法可以做到这一点,而我却不见了?
谢谢。
答案 0 :(得分:9)
read
返回一个list
,必须对其进行{em>评估才能对其进行funcall
编辑。
这可以使用read-time evaluation完成:
(funcall (read) 2)
#.(lambda (x) (* x x))
==> 4
但是,通常来说,这是一个安全漏洞(您正在评估用户提供的代码-如果他们键入#.(start-nuclear-war)
会怎样?),因此谨慎的工程师会将*read-eval*
绑定到nil
读取输入内容时,他们无法控制。
因此最好显式使用coerce
:
(funcall (coerce (let ((*read-eval* nil)) (read)) 'function) 2)
1+
==> 3
(funcall (coerce (let ((*read-eval* nil)) (read)) 'function) 2)
(lambda (x) (* x x))
==> 4
答案 1 :(得分:7)
由于使用的是read
,因此通常需要评估返回的表格以获得有意义的值。但是在特定情况下,您可以使用COERCE
。例如,从REPL:
CL-USER> (coerce '+ 'function)
#<FUNCTION +>
上面找到了符号+
为fbound的函数。
CL-USER> (coerce '(lambda (x) (* x x)) 'function)
#<FUNCTION (LAMBDA (X)) {53F2BF2B}>
上面的代码使用lambda expression并将其转换为功能对象。
答案 2 :(得分:3)
您会收到此错误,因为READ
仅返回列表 (LAMBDA (X) (* x x))
,而不会将其评估为函数。为此,您需要编写:
(funcall (eval (read)) 2)
请注意,尽管在那种情况下,仅写square
不再有效,用户现在需要输入#'square
。
答案 3 :(得分:2)
CL-USER 8 > (defun read-function (&optional (stream *standard-input*))
(let ((f (read stream)))
(cond (; function object
(functionp f) f)
(; symbol naming a function
(symbolp f) (symbol-function f))
(; (function f)
(and (consp f)
(eq (first f) 'function))
(eval f))
(; (lambda ...)
(and (consp f)
(eq (first f) 'lambda))
(eval f)))))
READ-FUNCTION
示例:
CL-USER 9 > (read-function)
#.#'+
#<Function + 40F0044AD4>
CL-USER 10 > (read-function)
+
#<Function + 40F0044AD4>
CL-USER 11 > (read-function)
#'+
#<Function + 40F0044AD4>
CL-USER 12 > (read-function)
(lambda (a b) (+ a b))
#<anonymous interpreted function 4060000C8C>