2017-06-15 1 views
0

私はCommon Lispを使用している間に問題を解決するためにネットを閲覧し、これを見つけました: Variable references in lisp これは私の問題に非常に関連しています。この部分を読んLisp /パラメータ渡し

は、機能を考えます! .................. コードは、a)スコープ内にあるか、b)スコープ内にある関数へのアクセス権が必要です。

私はそれを理解しようとしていますが、事柄が明確ではありません。

最初に私は最後の文でa)とb)が何を参照しているのか分かりません。

第2に、希望の結果を得るために提供されたコードの2つのチャンクを実際にどのように活用しますか?

関連するヒントを事前におねがいします。

+2

「所望の結果は何ですか?実際、あなたは何の質問をしていますか? – tfb

+0

基本的には、直接変数(範囲)または間接的に、変数のセットを閉じる(またはフィールド、配列を変更する)クロージャを呼び出すことによって、アクセスする変数を設定することができます。 http://www.gigamonkeys.com/book/variables.htmlを読んでください – coredump

答えて

1

私は依然としてあなたが求めている質問を解決することはできませんが、バインディングを見ることができない関数内のスコープ内でバインディングを修正する方法を知りたいと思います。それで、ここにその質問に対する答えがあります。

最初に理解しなければならないのは、現代のプログラミング言語の範囲が非常に単純であるということです。バインディング(名前と値の関連付け)が見える場合はアクセス権があり、変更可能な場合はそれ。プリモダンのプログラミング言語には、ずっと前の小さなコンピュータ(これはすべて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の結合は、それをキャプチャ関数の呼び出しによって変更されます。

しかし、これは文法的に大変です:明示的にfuncalllambdaを避けることができればうれしいです(私は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)が動作します。

そして、ここではこれを使用する上記foobar機能の再実装です:

(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)