2017-11-22 10 views
2

以下のコードのように、1つの引数を受け入れるクロージャを作成する関数を定義します。この値は、このクロージャのコンテキストでバインドされた変数を参照するシンボルになります。クロージャーの本体では、symbol-valueを使用してシンボルの値を取得しますが、エラーはSymbol's value as variable is voidと表示されます。このスニペットを評価すると123と表示されます。 symbol-valueが動作しないのはなぜElispでは、クロージャからローカルにバインドされたシンボルの値セルにアクセスする方法は?

  1. は、だからここに私は2つの質問がありますか?

  2. 希望の結果を得るためにこのスニペットを修正するにはどうすればよいですか?更新
    (defun make-closure() 
        (lexical-let ((var1 123)) 
        (lambda (name) 
         ;; How can I get the value cell of the symbol 
         ;; specified by the argument "name" ? 
         ;; This doesn't work. 
         (message-box (format "%s" (symbol-value name)))))) 
    
    (let ((closure (make-closure))) 
        (funcall closure 'var1)) 
    

:私は 模倣 "オブジェクト指向" にいくつかのおもちゃのコードを書いていたとき

実は、私はこの質問を得ました。最初は( はステファンの答えの第二のコードに似ている)、このようなものだった:

(defun new-person (initial-name) 
    (lexical-let* ((name initial-name) 
       (say-hi (lambda() 
          (message-box (format "Hi, I'm %s" name)))) 
       (change-name (lambda (new-name) 
           (setq name new-name)))) 
    (lambda (selector &rest args) 
     (cond 
     ((equal 'say-hi selector) (apply say-hi args)) 
     ((equal 'change-name selector) (apply change-name args)) 
     (t (message-box "Message not understood")))))) 

(let ((tony (new-person "Tony"))) 
    (funcall tony 'say-hi) 
    (funcall tony 'change-name "John") 
    (funcall tony 'say-hi)) 

しかし、私は「指揮」ちょっと「決まり文句」の句を感じ、私はそれが をすることは可能かもしれないと思いました

(defun new-person (initial-name) 
    (lexical-let* ((name initial-name) 
       (say-hi (lambda() 
          (message-box (format "Hi, I'm %s" name)))) 
       (change-name (lambda (new-name) 
           (setq name new-name)))) 
    (lambda (selector &rest args) 
     (apply (symbol-value selector) args)))) 

だから、我々が使用してはならないということである: は、もはや機能しており、次のようにそれを変更していないが、私はできません フィギュアアウトなぜ引数から渡されたシンボルを使用するので、I字句的に結合された変数を参照するシンボル。上記のように閉鎖してください。 の評価には、 が書かれているのと同じように保証されているわけではありません。

+0

字句変数は、単にクロージャの外側からアクセスできるようにはなっていません。それは多かれ少なかれそのポイントです。これらの変数にアクセスする必要がある場合は、最初はレキシカルバインディングが間違った選択だったので、それを回避しようとするのは良い考えではないと思います。 – phils

答えて

2

あなたはここにいくつかのことを誤解しましたが、重要なのは、あなたが(funcall closure 'var1)に渡しているvar1シンボルがあなたのラムダ関数が定義された字句の環境ではないシンボルであるということです。

字句解析フォームをマクロ展開すると、わかりやすくなります。これは:

(lexical-let ((var1 123)) 
    (lambda (name) 
    (message-box (format "%s" (symbol-value name))))) 

これらの線に沿って展開されます。

lexical-letマクロ 衝突しないようにしますが、バインディングで指定シンボル名を再書き込みということである
(progn 
    (defvar --cl-var1--) 
    (let ((--cl-var1-- 123)) 
    #'(lambda (name) 
     (message-box (format "%s" (symbol-value name)))))) 

実際にはが完了していないことに注意してください。は、var1というバインディングを持っています。完了していれば、コードに--cl-var1--という追加の参照があります。

あなたはこの関数にシンボルvar1を渡すときに、あなたがいない--cl-var1--(または何それは実際にことになった)、正規のvar1を渡しています。

これはまさにその通りです。レキシカルバインディングの性質は、そのスコープ内に記述されたコードに影響を与え、ではなく、が外部のコードに影響します。 (let ((closure (make-closure))) (funcall closure 'var1))フォームは外にあるため、字句限定のvar1はまったく表示されません。


それがコードを「修正」するために来るとき、私はむしろ、あなたがこれで行くことにしようとしている場所を把握するのに苦労していますが、あなたので、私の解釈であなたは、まったく閉鎖を望んでいないだろうレキシカルバインディングではなく動的バインディングを探しています。例えば:

(defun make-func() 
    (lambda (name) 
    (message-box (format "%s" (symbol-value name))))) 

(let ((func (make-func)) 
     (var1 123)) 
    (funcall func 'var1)) 

質問への編集に基づいて、私はあなたが関数の引数に一致しようとしている値のための字句バインディングを使用していないように、少しのコードを作り直すことをお勧め。例えば:

(defun new-person (initial-name) 
    (lexical-let* 
     ((name initial-name) 
     (map (list 
      (cons 'say-hi (lambda() 
          (message-box 
           (format "Hi, I'm %s" name)))) 
      (cons 'change-name (lambda (new-name) 
            (setq name new-name)))))) 
    (lambda (selector &rest args) 
     (apply (cdr (assq selector map)) args)))) 
+0

+1最後の段落と最終的なコードのために、そして質問があまりにも明確なwrt意図ではないことを言及するために。 – Drew

1

字句-バインド変数は、基本的には名前がありません(すなわち、その名が評価時のソースコード内には存在しますが、不在で唯一の一時的な人工物です)。

;; -*- lexical-binding:t -*- 

(defun make-closure() 
    (lambda (ref) 
    ;; How can I get the value cell of the symbol 
    ;; specified by the argument "name" ? 
    ;; This doesn't work. 
    (message-box (format "%s" (gv-deref ref))))) 

(let ((closure (make-closure))) 
    (let ((var1 123)) 
    (funcall closure (gv-ref var1)))) 

をしかし、私はそう、私は外からそれへのREFを取得することはできませんのでvar1のletバインディングを移動しなければならなかったことに注意してください:

代わりに変数に参照を使用することができます。

(defun make-closure() 
    (lexical-let ((var1 123)) 
    (lambda (name) 
     ;; How can I get the value cell of the symbol 
     ;; specified by the argument "name" ? 
     ;; This doesn't work. 
     (message-box (format "%s" (pcase name 
           ('var1 var1) 
           (_ (error "Unknown var name %S" name)))))))) 

(let ((closure (make-closure))) 
    (funcall closure 'var1)) 

私は2つの異なる目的のためにvar1を使用注:それは字句VARの名前だと、それは相手の時間を手動で自分のレキシカル変数に名前を付けるために別のオプションがある

どのvarを使用するかを選択するためのシンボルと、pcaseのthingyが相互に変換します。字句にバインドされたvarに "any"という別の名前を使用すると、コードは同じようにうまくいきます(外部呼び出し元を変更する必要はありません) 。

関連する問題