SBCL 和 Lambda 表达式
SBCL & Lambda Expressions
在 SBCL 中,如何将 lambda 表达式放入结构槽 [例如,(setf (struct-slot1 struct1) '(lambda (x) (* x x)))
],以便可以用 funcall
或 apply
调用它? SBCL 编译器抱怨:wanted one of (FUNCTION SYMBOL)
。有没有办法安装这样的 lambda 而无需编译它或给它一个明确的名称?为什么 lambda 表达式 不是函数?我想做类似的事情:(funcall (function (struct-slot1 struct1)) 3)
。感谢您的任何见解。 (ps:通常我在 运行 之前编译 lambda,但在调试期间我需要查看 lambda 的内部结构。)
这不是 SBCL 特定的,而是根据 ANSI Common Lisp 标准。
将 Lambda 表达式 作为列表转换为函数
这些是您的选择:
CL-USER 168 > (funcall (coerce '(lambda (x) (* x x))
'function)
4)
16
CL-USER 169 > (funcall (compile nil '(lambda (x) (* x x)))
4)
16
CL-USER 170 > (funcall (eval '(lambda (x) (* x x))) ; because LAMBDA is a macro, too
4)
16
CL-USER 171 > (funcall (eval '(function (lambda (x) (* x x))))
4)
16
请注意,lambda 表达式引用了 空词法环境 。因此它无法访问周围代码中的任何词法变量。
Lambda 表达式不是函数对象
Why is a lambda expression not a function?
因为它只是一个列表,而不是代码。要将 lambda 表达式转换为代码,您必须将其转换为函数对象。
其他一些(通常较旧的)Lisp 允许您将 lambda 表达式用作代码,但 Common Lisp 不允许。这是由标准定义的。
你做不到(funcall '(lambda (x) (+ x x)) 3)
。您必须先将 lambda 表达式转换为函数对象。
FUNCTION是一个特殊的运算符->syntax+semantics
(funcall (function (struct-slot1 struct1)) 3)
这已经是语法错误了。 FUNCTION
是一个 特殊运算符 ,需要一个 函数名 或一个 lambda 表达式 。 (struct-slot1 struct1)
两者都不是。 (struct-slot1 struct1)
是从结构中检索值的代码。但它不是 函数名 ,而是 符号 或列表 (setf <some-symbol>)
。它也不是 lambda 表达式,它类似于 (lambda (...) ...)
.
(ps: normally I compile the lambda before running, but during debugging I need to see the innards of the lambda.)
试试这个:
(proclaim '(optimize (debug 3)))
在SBCL中,您还可以这样做:
(sb-ext:restrict-compiler-policy 'debug 3)
... 建立最低允许的调试策略。
文档说:
See also :POLICY option in WITH-COMPILATION-UNIT.
现在,为测试定义一个简单的散列table:
CL-USER> (defparameter *hash* (make-hash-table :test #'eq))
插入一个匿名函数:
CL-USER> (setf (gethash :fun *hash*) (lambda (u) (* 10 u)))
#<FUNCTION (LAMBDA (U)) {1005140E2B}>
骂得不好:
CL-USER> (funcall (gethash :fun *hash*) "oops")
调试器出现:
Argument X is not a NUMBER: "oops"
[Condition of type SIMPLE-TYPE-ERROR]
Restarts:
0: [RETRY] Retry SLIME REPL evaluation request.
1: [*ABORT] Return to SLIME's top level.
2: [ABORT] abort thread (#<THREAD "repl-thread" RUNNING {10041C8033}>)
Backtrace:
0: (SB-KERNEL:TWO-ARG-* "oops" 10)
1: ((LAMBDA (U)) "oops")
2: (SB-INT:SIMPLE-EVAL-IN-LEXENV (FUNCALL (GETHASH :FUN *HASH*) "oops") #<NULL-LEXENV>)
3: (EVAL (FUNCALL (GETHASH :FUN *HASH*) "oops"))
转到第 1 帧,((LAMBDA (U)) "oops")
,然后按 v(a.k.a。sldb-show-source
)。弹出一个新缓冲区,内容如下:
(LAMBDA ()
(PROGN
(LET* ((#:HASHTABLE *HASH*) (#:NEW1 (LAMBDA (U) (#:***HERE*** (* 10 U)))))
(SB-KERNEL:%PUTHASH :FUN #:HASHTABLE #:NEW1))))
由于调试器策略足够高,匿名函数的源代码是可用的,即使它是在 REPL 中键入的。如果您的匿名函数恰好是从源文件编译的,则按 v 将转到该文件中的适当位置(对应于包含在 ***HERE***
术语中的形式以上)。
如果需要,您也可以在编译之前将代码存储为数据:
(defun wrap-and-compile (code)
(let ((function (compile nil code)))
(compile nil `(lambda (&rest args)
(restart-case (apply ,function args)
(DEBUG () :report ,(format nil "~S" code)
',code))))))
(setf (gethash :wrapped *hash*)
(wrap-and-compile '(lambda (x) (* 10 x))))
(funcall (gethash :wrapped *hash*) "Boo")
并且调试器显示:
Argument X is not a NUMBER: "Boo"
[Condition of type SIMPLE-TYPE-ERROR]
Restarts:
0: [DEBUG] (LAMBDA (X) (* 10 X))
1: [RETRY] Retry SLIME interactive evaluation request.
2: [*ABORT] Return to SLIME's top level.
3: [ABORT] abort thread (#<THREAD "worker" RUNNING {100513A403}>)
这有点老套,但它确实有效。 DEBUG 重新启动 returns 编译函数的原始数据。
在 SBCL 中,如何将 lambda 表达式放入结构槽 [例如,(setf (struct-slot1 struct1) '(lambda (x) (* x x)))
],以便可以用 funcall
或 apply
调用它? SBCL 编译器抱怨:wanted one of (FUNCTION SYMBOL)
。有没有办法安装这样的 lambda 而无需编译它或给它一个明确的名称?为什么 lambda 表达式 不是函数?我想做类似的事情:(funcall (function (struct-slot1 struct1)) 3)
。感谢您的任何见解。 (ps:通常我在 运行 之前编译 lambda,但在调试期间我需要查看 lambda 的内部结构。)
这不是 SBCL 特定的,而是根据 ANSI Common Lisp 标准。
将 Lambda 表达式 作为列表转换为函数
这些是您的选择:
CL-USER 168 > (funcall (coerce '(lambda (x) (* x x))
'function)
4)
16
CL-USER 169 > (funcall (compile nil '(lambda (x) (* x x)))
4)
16
CL-USER 170 > (funcall (eval '(lambda (x) (* x x))) ; because LAMBDA is a macro, too
4)
16
CL-USER 171 > (funcall (eval '(function (lambda (x) (* x x))))
4)
16
请注意,lambda 表达式引用了 空词法环境 。因此它无法访问周围代码中的任何词法变量。
Lambda 表达式不是函数对象
Why is a lambda expression not a function?
因为它只是一个列表,而不是代码。要将 lambda 表达式转换为代码,您必须将其转换为函数对象。
其他一些(通常较旧的)Lisp 允许您将 lambda 表达式用作代码,但 Common Lisp 不允许。这是由标准定义的。
你做不到(funcall '(lambda (x) (+ x x)) 3)
。您必须先将 lambda 表达式转换为函数对象。
FUNCTION是一个特殊的运算符->syntax+semantics
(funcall (function (struct-slot1 struct1)) 3)
这已经是语法错误了。 FUNCTION
是一个 特殊运算符 ,需要一个 函数名 或一个 lambda 表达式 。 (struct-slot1 struct1)
两者都不是。 (struct-slot1 struct1)
是从结构中检索值的代码。但它不是 函数名 ,而是 符号 或列表 (setf <some-symbol>)
。它也不是 lambda 表达式,它类似于 (lambda (...) ...)
.
(ps: normally I compile the lambda before running, but during debugging I need to see the innards of the lambda.)
试试这个:
(proclaim '(optimize (debug 3)))
在SBCL中,您还可以这样做:
(sb-ext:restrict-compiler-policy 'debug 3)
... 建立最低允许的调试策略。 文档说:
See also :POLICY option in WITH-COMPILATION-UNIT.
现在,为测试定义一个简单的散列table:
CL-USER> (defparameter *hash* (make-hash-table :test #'eq))
插入一个匿名函数:
CL-USER> (setf (gethash :fun *hash*) (lambda (u) (* 10 u)))
#<FUNCTION (LAMBDA (U)) {1005140E2B}>
骂得不好:
CL-USER> (funcall (gethash :fun *hash*) "oops")
调试器出现:
Argument X is not a NUMBER: "oops"
[Condition of type SIMPLE-TYPE-ERROR]
Restarts:
0: [RETRY] Retry SLIME REPL evaluation request.
1: [*ABORT] Return to SLIME's top level.
2: [ABORT] abort thread (#<THREAD "repl-thread" RUNNING {10041C8033}>)
Backtrace:
0: (SB-KERNEL:TWO-ARG-* "oops" 10)
1: ((LAMBDA (U)) "oops")
2: (SB-INT:SIMPLE-EVAL-IN-LEXENV (FUNCALL (GETHASH :FUN *HASH*) "oops") #<NULL-LEXENV>)
3: (EVAL (FUNCALL (GETHASH :FUN *HASH*) "oops"))
转到第 1 帧,((LAMBDA (U)) "oops")
,然后按 v(a.k.a。sldb-show-source
)。弹出一个新缓冲区,内容如下:
(LAMBDA ()
(PROGN
(LET* ((#:HASHTABLE *HASH*) (#:NEW1 (LAMBDA (U) (#:***HERE*** (* 10 U)))))
(SB-KERNEL:%PUTHASH :FUN #:HASHTABLE #:NEW1))))
由于调试器策略足够高,匿名函数的源代码是可用的,即使它是在 REPL 中键入的。如果您的匿名函数恰好是从源文件编译的,则按 v 将转到该文件中的适当位置(对应于包含在 ***HERE***
术语中的形式以上)。
如果需要,您也可以在编译之前将代码存储为数据:
(defun wrap-and-compile (code)
(let ((function (compile nil code)))
(compile nil `(lambda (&rest args)
(restart-case (apply ,function args)
(DEBUG () :report ,(format nil "~S" code)
',code))))))
(setf (gethash :wrapped *hash*)
(wrap-and-compile '(lambda (x) (* 10 x))))
(funcall (gethash :wrapped *hash*) "Boo")
并且调试器显示:
Argument X is not a NUMBER: "Boo"
[Condition of type SIMPLE-TYPE-ERROR]
Restarts:
0: [DEBUG] (LAMBDA (X) (* 10 X))
1: [RETRY] Retry SLIME interactive evaluation request.
2: [*ABORT] Return to SLIME's top level.
3: [ABORT] abort thread (#<THREAD "worker" RUNNING {100513A403}>)
这有点老套,但它确实有效。 DEBUG 重新启动 returns 编译函数的原始数据。