Lambda SBCL&;表达式

Lambda SBCL&;表达式,lambda,common-lisp,sbcl,Lambda,Common Lisp,Sbcl,在SBCL中,如何将lambda表达式放入结构槽[例如,(setf(struct-slot1 struct1)'(lambda(x)(*x))],以便可以使用funcall或apply调用它?SBCL编译器抱怨:需要一个(函数符号)。有没有一种方法可以安装这样一个lambda而不必编译它或给它一个明确的名称?为什么lambda表达式不是函数?我想做如下操作:(funcall(function(struct-slot1 struct1))3)。谢谢你的见解。(注:通常我在运行之前编译lambda,

在SBCL中,如何将lambda表达式放入结构槽[例如,
(setf(struct-slot1 struct1)'(lambda(x)(*x))
],以便可以使用
funcall
apply
调用它?SBCL编译器抱怨:
需要一个(函数符号)
。有没有一种方法可以安装这样一个lambda而不必编译它或给它一个明确的名称?为什么lambda表达式不是函数?我想做如下操作:
(funcall(function(struct-slot1 struct1))3)
。谢谢你的见解。(注:通常我在运行之前编译lambda,但在调试期间,我需要查看lambda的内部。)

这不是特定于SBCL的,而是根据ANSI通用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表达式不是函数对象

为什么lambda表达式不是函数

因为它只是一个列表,而不是代码。要将lambda表达式转换为代码,必须将其转换为函数对象

其他一些(通常较旧的)Lisp允许您使用lambda表达式作为代码,但不允许使用公共Lisp。这是由标准定义的

您不能执行
(funcall'(lambda(x)(+x))3)
。必须首先将lambda表达式转换为函数对象

函数是一个特殊的运算符->语法+语义

(funcall(函数(struct-slot1 struct1))3)

这已经是一个语法错误<代码>函数是一个特殊运算符,需要函数名或lambda表达式<代码>(struct-slot1 struct1)两者都不是<代码>(struct-slot1 struct1)是从结构中检索值的代码。但它不是一个函数名,而是一个符号或一个列表
(setf)
。它也不是lambda表达式,类似于
(lambda(…)…)

(ps:通常我在运行之前编译lambda,但在调试期间,我需要查看lambda的内部。)

试试这个:

(proclaim '(optimize (debug 3)))
在SBCL中,您还可以执行以下操作:

(sb-ext:restrict-compiler-policy 'debug 3)
。。。它为调试建立了允许的最低策略。 文件说:

另请参见WITH-COMPUTING-UNIT中的策略选项

现在,为测试定义一个简单的哈希表:

CL-USER> (defparameter *hash* (make-hash-table :test #'eq))
插入匿名函数:

CL-USER> (setf (gethash :fun *hash*) (lambda (u) (* 10 u)))
#<FUNCTION (LAMBDA (U)) {1005140E2B}>
调试器将显示:

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"))
由于调试器策略足够高,匿名函数的源代码可用,即使它是在REPL中键入的。如果您的匿名函数恰好是从源文件编译而来,则按v键将转到该文件中的相应点(对应于上面用
***术语包装的表单)


如果需要,还可以在编译代码之前将其存储为数据:

(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}>)
参数X不是数字:“Boo”
[类型SIMPLE-type-ERROR的条件]
重新启动:
0:[调试](λ(X)(*10 X))
1:[重试]重试SLIME交互式评估请求。
2:[*中止]返回SLIME的顶层。
3:[中止]中止线程(#)

这有点老套,但很管用。重新启动调试将返回编译函数的原始数据。

您的用例不清楚您是从某处读取输入还是从什么地方读取输入。通常您只需执行
(setf(struct-slot1 struct1)(lambda(x)(*x)))
。然后你可以
(funcall(struct-slot1 struct1)2)=>4
很抱歉搞混了(但是我搞混了!)我正试图用setf做一些你建议的事情,但是我需要构造lambda表达式,所以它看起来大概像(setf(struct-slot1 struct1)`(lambda(x)(*x,(+2)),它安装的是一个列表,而不是一个函数。但正如Rainer评论的那样,您可以使用强制将列表转换为函数[然后可以使用(函数lambda list(struct-slot1 struct1)]访问lambda表达式。(为了重新输入记录,我正在处理一个用户的spec文件,其中包含一些构建这些lambda表达式的宏。)我也不确定我是否理解这个用例。为什么不干脆
(setf…(let((y(+2)))(lambda(x)(*xy)))
?谢谢雷纳。我忘记了强制——这就解决了问题。也谢谢你的清晰解释,根据我的观点,它比实际的公共Lisp>函数>匿名函数要好。第一个示例中的
强制是必要的吗?我看到
类型为(lambda(x)(*x x))
已经是
函数了。@agam:这个例子是关于如何从作为数据列表的代码到有效函数->从
(引号(lambda()…)
到函数。
(lambda()…)
已经是函数了,因为它没有引号。有趣的是,我的匿名函数是构造的[并且在加载时可选地使用(compile nil)],因此它们本身没有源代码。您的评论让我确信,有志者事竟成!@davypough只是为了确保我尝试过这样构建的函数:
(compile nil(list'lambda(list'x)(list'*'x 10)))
这仍然有效。但在任何情况下,您都可以在编译函数之前创建自己的工具,将其存储为数据。请参阅编辑。
(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}>)