私は依然としてあなたが求めている質問を解決することはできませんが、バインディングを見ることができない関数内のスコープ内でバインディングを修正する方法を知りたいと思います。それで、ここにその質問に対する答えがあります。
最初に理解しなければならないのは、現代のプログラミング言語の範囲が非常に単純であるということです。バインディング(名前と値の関連付け)が見える場合はアクセス権があり、変更可能な場合はそれ。プリモダンのプログラミング言語には、ずっと前の小さなコンピュータ(これはすべてPDP-11の遺産に呪われている)での実装の容易さに基づいてこれを制限するあらゆる種類の秘密の規則がありますが、現代のものはすべてこれを掃除します。 Common Lispは、ほとんどの場合、現代のプログラミング言語です。
何をする必要があるのは何とかバインディングをキャプチャし、そのキャプチャされたバインディングを呼び出す関数にアクセスしたり、変更したりすることができます。バインディングをキャプチャする方法は、関数で行います。
だから、ここCLでこれを行う方法の簡単な例です。このコードで
(defun foo (orig new)
(let ((x orig))
(bar (lambda (&optional (value nil valuep))
(if valuep
(setf x value)
x))
new)
x))
(defun bar (c new)
(format t "~&initially ~S~%" (funcall c))
(funcall c new)
(format t "~&then ~S~%" (funcall c)))
bar
に最初の引数によって作成された関数はx
の結合へのアクセス権を持っている、と書かれているので、引数なしで呼び出すとバインディングの値が返され、引数を指定して呼び出すと値が設定されます。ここにいることはアクションである:
CL-USER 5 > (foo 1 2)
initially 1
then 2
2
ですから、この作品見ることができます:x
の結合は、それをキャプチャ関数の呼び出しによって変更されます。
しかし、これは文法的に大変です:明示的にfuncall
とlambda
を避けることができればうれしいです(私はLisp-1で前者を避けることができますが、まだそれほど良いことではありません)。だからここ(以下、それについての説明)ことを行い、いくつかのコード:
(defmacro capture (binding)
"Capture a binding"
(let ((value (make-symbol "VALUE"))
(valuep (make-symbol "VALUEP")))
`(lambda (&optional (,value nil ,valuep))
(if ,valuep
(setf ,binding ,value)
,binding))))
(defun captured (c &optional (value nil valuep))
"Return the value of a captured binding, or set it"
(if valuep
(funcall c value)
(funcall c)))
(defsetf captured captured)
OK、そうマクロcapture
は、元のコードのものと同一の機能を作成するだけの構文糖です。関数はキャプチャしているバインディングのスコープ内に作成する必要があるため、マクロでなければなりません。そうではなく、我々は(captured c)
を言うことができる(funcall c)
と言って:
captured
は、些細なだけの適切な方法でcapture
によって作成された関数を呼び出す関数です。
最後に、defsetf
のフォームはキャプチャされたバインディングを設定する方法を教えているので、(setf (captured x) y)
が動作します。
そして、ここではこれを使用する上記foo
とbar
機能の再実装です:
(defun foo (orig new)
(let ((x orig))
(bar (capture x) new)
x))
(defun bar (c new)
(format t "~&initially ~S~%" (captured c))
(setf (captured c) new)
(format t "~&then ~S~%" (captured c)))
私はそれが、これは上記のすべての明示的funcall
sおよびlambda
秒よりも読み取ることが進歩していることは明らかだと思います。ところで、あなたはそう長くsetf
は彼らと何をすべきかを知っているとして(限り、彼らは何もしているように、当然のexpresssionsだけではなく、変数バインディングを、キャプチャすることができ
CL-USER 6 > (foo 1 2)
initially 1
then 2
2
:そしてそれは同じように動作します呼び出し[場所]):
(defun fish (l)
(bone (capture (car l)))
l)
(defun bone (c)
(setf (captured c) 'bone))
そして今
CL-USER 13 > (fish (list 1 2))
(bone 2)
「所望の結果は何ですか?実際、あなたは何の質問をしていますか? – tfb
基本的には、直接変数(範囲)または間接的に、変数のセットを閉じる(またはフィールド、配列を変更する)クロージャを呼び出すことによって、アクセスする変数を設定することができます。 http://www.gigamonkeys.com/book/variables.htmlを読んでください – coredump