【发布时间】:2012-02-29 23:24:07
【问题描述】:
假设我有一个类似这个简单示例的花园式闭包:
(let ((alpha 0) #| etc. |# )
(lambda ()
(incf alpha)
#| more code here |#
alpha))
假设我 (funcall) 该闭包的一个实例三次,并且在第三次执行的中间,这个闭包想要将自己保存在某个地方(例如在哈希表中)。然后我暂时不(funcall)这个实例。然后我从哈希表中检索这个实例并再次(funcall),得到返回值 4。
闭包中的函数如何引用自身,以便将自己保存在该哈希表中?
编辑 1:这是一个更详细的示例。我通过将闭包作为参数传递给自身来实现目标。但我希望闭包能够在不进行自参数化的情况下自行完成所有这些操作。
1 (defparameter *listeriosis* nil)
2 (defparameter *a*
3 (lambda ()
4 (let ((count 0))
5 (lambda (param1 param2 param3 self)
6 (incf count)
7 (when (= 3 count)
8 (push self *listeriosis*)
9 (push self *listeriosis*)
10 (push self *listeriosis*))
11 count))))
12 (let ((bee (funcall *a*)))
13 (princ (funcall bee 1 2 3 bee)) (terpri)
14 (princ (funcall bee 1 2 3 bee)) (terpri)
15 (princ (funcall bee 1 2 3 bee)) (terpri)
16 (princ (funcall bee 1 2 3 bee)) (terpri)
17 (princ (funcall bee 1 2 3 bee)) (terpri))
18 (princ "///") (terpri)
19 (princ (funcall (pop *listeriosis*) 1 2 3 nil)) (terpri)
20 (princ (funcall (pop *listeriosis*) 1 2 3 nil)) (terpri)
21 (princ (funcall (pop *listeriosis*) 1 2 3 nil)) (terpri)
1
2
3
4
5
///
6
7
8
编辑 2:是的,我知道我可以使用宏将函数的名称作为第一个参数插入,然后使用该宏而不是 (funcall),但我仍然想知道如何让闭包引用它自己的实例。
编辑 3:为了回应 SK-logic 的友好建议,我做了以下事情,但它没有做我想要的。它将三个新的闭包推入堆栈,而不是对同一个闭包的三个引用。看看当我从堆栈中弹出它们时,调用的值是 1、1 和 1 而不是 6、7 和 8?
1 (defparameter *listeriosis* nil)
2 (defun Y (f)
3 ((lambda (x) (funcall x x))
4 (lambda (y)
5 (funcall f (lambda (&rest args)
6 (apply (funcall y y) args))))))
7 (defparameter *a*
8 (lambda (self)
9 (let ((count 0))
10 (lambda (param1 param2 param3)
11 (incf count)
12 (when (= 3 count)
13 (push self *listeriosis*)
14 (push self *listeriosis*)
15 (push self *listeriosis*))
16 count))))
17 (let ((bee (Y *a*)))
18 (princ (funcall bee 1 2 3 #| bee |# )) (terpri)
19 (princ (funcall bee 1 2 3 #| bee |# )) (terpri)
20 (princ (funcall bee 1 2 3 #| bee |# )) (terpri)
21 (princ (funcall bee 1 2 3 #| bee |# )) (terpri)
22 (princ (funcall bee 1 2 3 #| bee |# )) (terpri))
23 (princ "///") (terpri)
24 (princ (funcall (pop *listeriosis*) 1 2 3)) (terpri)
25 (princ (funcall (pop *listeriosis*) 1 2 3)) (terpri)
26 (princ (funcall (pop *listeriosis*) 1 2 3)) (terpri)
1
2
3
4
5
///
1
1
1
编辑 4:Jon O 的建议恰到好处。这是代码和输出:
1 (defparameter *listeriosis* nil)
2 (defparameter *a*
3 (lambda ()
4 (let ((count 0))
5 (labels ((self (param1 param2 param3)
6 (incf count)
7 (when (= 3 count)
8 (push (function self) *listeriosis*)
9 (push (function self) *listeriosis*)
10 (push (function self) *listeriosis*))
11 count))
12 (function self)))))
13 (let ((bee (funcall *a*)))
14 (princ (funcall bee 1 2 3)) (terpri)
15 (princ (funcall bee 1 2 3)) (terpri)
16 (princ (funcall bee 1 2 3)) (terpri)
17 (princ (funcall bee 1 2 3)) (terpri)
18 (princ (funcall bee 1 2 3)) (terpri))
19 (princ "///") (terpri)
20 (princ (funcall (pop *listeriosis*) 1 2 3)) (terpri)
21 (princ (funcall (pop *listeriosis*) 1 2 3)) (terpri)
22 (princ (funcall (pop *listeriosis*) 1 2 3)) (terpri)
1
2
3
4
5
///
6
7
8
编辑 5:Miron 的建议也恰到好处,实际上使代码更具可读性:
1 (defmacro alambda (parms &body body)
2 `(labels ((self ,parms ,@body))
3 #'self))
4 ;
5 (defparameter *listeriosis* nil)
6 (defparameter *a*
7 (lambda ()
8 (let ((count 0))
9 (alambda (param1 param2 param3)
10 (incf count)
11 (when (= 3 count)
12 (push #'self *listeriosis*)
13 (push #'self *listeriosis*)
14 (push #'self *listeriosis*))
15 count))))
16 ;
17 (let ((bee (funcall *a*)))
18 (princ (funcall bee 1 2 3)) (terpri)
19 (princ (funcall bee 1 2 3)) (terpri)
20 (princ (funcall bee 1 2 3)) (terpri)
21 (princ (funcall bee 1 2 3)) (terpri)
22 (princ (funcall bee 1 2 3)) (terpri))
23 (princ "///") (terpri)
24 (princ (funcall (pop *listeriosis*) 1 2 3)) (terpri)
25 (princ (funcall (pop *listeriosis*) 1 2 3)) (terpri)
26 (princ (funcall (pop *listeriosis*) 1 2 3)) (terpri)
1
2
3
4
5
///
6
7
8
【问题讨论】:
-
我假设“关闭”是指“匿名函数”。无论如何,我不明白你为什么不简单地给它起个名字?
-
我不想要一个新的闭包实例。我想要旧的,不断更改封闭变量。事实上,可能有几个实例,每个实例都应该在某个地方加盐。这一切都可以在给它起名字的同时完成吗?语法是什么?
-
我不太会说 lisp,但我想 JavaScript 中常见的“自执行函数表达式”之类的东西可以解决问题。
-
@BillEvansatMariposa,将您的闭包定义为
(lambda (self) (lambda (args ...) ...)),然后将Y 应用为(Y #'whatever)并存储结果。然后,self将是对闭包本身的引用。
标签: lisp common-lisp clisp