5

このベアボーンサンプルのような庭の種類のクロージャーがあるとします。

(let ((alpha 0) #| etc. |# )
  (lambda ()
    (incf alpha)
    #| more code here |#
    alpha))

そのクロージャ(funcall)のインスタンスを3回実行し、3回目の実行の途中で、このクロージャが自分自身をどこかに保存したいとします(たとえば、ハッシュテーブル内)。(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の親切な提案に応えて、私は次のことをしましたが、それは私が望むことをしません。同じクロージャへの3つの参照ではなく、スタックに3つの新しいクロージャをプッシュします。それらをスタックからポップすると、呼び出しの値が6、7、および8ではなく1、1、および1になる方法を参照してください。

 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:JonOの提案は正確に的を射た。コードと出力は次のとおりです。

 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
4

2 に答える 2

7

これを行うために、Yコンビネータを自分で定義する必要はないと思います。組み込みのlabelsフォームは、必要な自己参照バインディングを作成します。HyperSpecによると:

「ラベルに定義された関数名のスコープが、本体だけでなく関数定義自体も含むことlabelsを除いて、同等です。」flet

これはみんなのお気に入りのおもちゃのクロージャの例で、ローカルで定義されfたものがそれ自体のバインディングをどのように閉じるかを示しています。

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

これは、カウンターの新しい値とそれ自体の関数値の2つの値を返すクロージャーを返します。使用例:

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)

クロージャを返すのではなく、データ構造に格納するように拡張するのは簡単です。

于 2012-02-08T15:46:00.393 に答える
5

alambda(On Lispにもあります)はどうですか?

;; Graham's alambda
(defmacro alambda (parms &body body)
  `(labels ((self ,parms ,@body))
     #'self))
于 2012-02-08T18:47:35.530 に答える