封闭如何引用自身?

时间:2012-02-08 14:01:57

标签: lisp common-lisp clisp

假设我有一个花园式的闭包,就像这个简单的样本:

(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

2 个答案:

答案 0 :(得分:7)

我不认为你需要为自己定义Y组合器才能做到这一点;内置的labels表单将创建您需要的自引用绑定。根据{{​​3}}:

labels等同于flet,除了标签的已定义函数名称的范围包含函数定义本身以及正文。”

这是每个人最喜欢的玩具封闭示例,显示本地定义的f如何关闭其自己的绑定:

(defun make-counter (n)
   (labels ((f () (values (incf n) (function f))))
     (function f)))

这将返回一个闭包,它返回两个值:计数器的新值和它自己的函数值。使用示例:

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)

将它扩展到将闭包存储在数据结构中而不是返回它应该是直截了当的。

答案 1 :(得分:4)

alambda(也在On Lisp中)怎么样?

;; Graham's alambda
(defmacro alambda (parms &body body)
  `(labels ((self ,parms ,@body))
     #'self))