2012-02-08 11 views
5

は、私は、この最低限のサンプルのようなありふれたクロージャがあるとします。クロージャーはどのようにしてクロージャーを参照できますか?

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

はI (funcall)その閉鎖のインスタンス三回と仮定し、3回目の実行の途中で、この閉鎖は自分自身を保存したいですどこかで(ハッシュテーブルの中で)私はしばらくの間このインスタンス(funcall)をしないでください。次に、ハッシュテーブルからこのインスタンスを取得し、戻り値が(funcall)であるため、戻り値は4になります。

クロージャの関数は、それ自身を参照してそのハッシュテーブルに保存できますか?

EDIT 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 

EDIT 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 

EDIT 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 

EDIT 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 
+0

「クロージャ」とは、「匿名機能」を意味します。いずれにせよ、なぜあなたは単にそれに名前を付けるだけではないのか分かりません。 – delnan

+0

私はクロージャーの新しいインスタンスを望んでいません。私は古い変数を囲み変数に続けて変更したい。実際には、いくつかのインスタンスが存在する可能性があります。インスタンスのそれぞれは、どこかで塩分を除去する必要があります。名前を付けてこれを行うことはできますか?構文は何でしょうか? –

+0

私はlispをうまく話せませんが、JavaScriptでよく使われる「自己実行関数式」のようなものがこのトリックを行うと思います。 – delnan

答えて

4

alambda(On Lispにもあります)について教えてください。

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

私はこの問題のEDIT 5でこれを使用しました。実際には、コードをさらに読みやすくしています。ありがとう! –

+2

これは、「ラベル」のアイデアの最も一般的な実装です。 'blambda'が便利で、' self'という名前を付けることもできます。この例の詳細は、[Alexandria](http://common-lisp.net/project/alexandria/draft/alexandria.html)を参照してください( 'if-let'を探してください)。 –

+2

例 'blambda':'(defmacro blambda(fn-name args&body body) \ '(ラベル((、fn-name、args、@ body)) # '、fn-name))' (funcall(blaxda fact(x)(if(= 0 x)1(* x(事実(1- x))))))5) ' –

7

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

は、fletに相当しますが、ラベルの定義された関数名の範囲は、本体と同様に関数定義自体を含みます。

はここでローカルに定義されたfの上に閉じ様子、みんなのお気に入りのおもちゃの閉鎖例だ独自のバインディング:カウンタの新しい値、および独自の:これは、2つの値を返すクロージャを返す

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

関数値です。使用例:クロージャを返すのではなく、データ構造に格納することは、クロージャを拡張するのが簡単なはずです。

+0

パーフェクト! (編集した質問の結果を参照してください)ありがとう! –

+0

うれしかったことがうれしい! –

+1

@Bill:Related(but duplicate):http://stackoverflow.com/q/7936024/13これは 'labels'や' letrec'やYコンビネータなどが必要な理由を説明しています。 (開示:受け入れられた答えを書いた) –

関連する問題