Bil*_*osa 5 lisp clisp common-lisp
假设我有一个像这样的裸骨样本的花园式闭包:
(let ((alpha 0) #| etc. |# )
(lambda ()
(incf alpha)
#| more code here |#
alpha))
Run Code Online (Sandbox Code Playgroud)
假设我(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
Run Code Online (Sandbox Code Playgroud)
编辑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
Run Code Online (Sandbox Code Playgroud)
编辑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
Run Code Online (Sandbox Code Playgroud)
编辑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
Run Code Online (Sandbox Code Playgroud)
我不认为你需要为自己定义Y组合器才能做到这一点; 内置的labels
表单将创建您需要的自引用绑定.根据HyperSpec:
" labels
等同flet
于标签的已定义函数名称的范围包含函数定义本身以及正文."
这是每个人最喜欢的玩具封闭示例,显示了本地定义如何f
关闭其自己的绑定:
(defun make-counter (n)
(labels ((f () (values (incf n) (function f))))
(function f)))
Run Code Online (Sandbox Code Playgroud)
这将返回一个闭包,它返回两个值:计数器的新值和它自己的函数值.使用示例:
CL-USER> (setq g (make-counter 5))
#<FUNCTION F NIL (BLOCK F (VALUES (INCF N) #'F))>
CL-USER> (multiple-value-bind (n q) (funcall g) (list n (funcall q)))
(6 7)
Run Code Online (Sandbox Code Playgroud)
将数据结构中的闭包存储起来而不是返回它应该是直截了当的.
什么alambda(也是Lisp的)?
;; Graham's alambda
(defmacro alambda (parms &body body)
`(labels ((self ,parms ,@body))
#'self))
Run Code Online (Sandbox Code Playgroud)