2017-11-22 5 views
2

Wie im folgenden Code definiert ich eine Funktion zum Erstellen einer Closure, die ein Argument akzeptiert, dessen Wert ein Symbol sein soll, das sich auf eine Variable bezieht, die im Kontext dieser Closure gebunden ist. Im Körper des Verschlusses verwende ich symbol-value, um den Wert des Symbols zu erhalten, aber es erscheint ein Fehler, der Symbol's value as variable is void sagt, ich erwarte, dass dieses Schnipsel 123 anzeigt.In Elisp, wie auf die Wertzelle eines Symbols zugreifen, das lokal von einem Abschluss gebunden ist?

hier So habe ich zwei Fragen:

  1. Warum symbol-value funktioniert nicht?
  2. Wie korrigiere ich dieses Snippet, um das gewünschte Ergebnis zu erhalten?
(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)) 

Aktualisiert:

Eigentlich habe ich diese Frage, als ich einige Spielzeug-Code ahmen "Object Oriented" zu schreiben. Zuerst war es so etwas wie dies (die auf den zweiten Code von Stefan Antwort ähnlich ist):

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

Aber fühlte ich die Klauseln „cond“ irgendwie „vorformulierten“ und ich dachte, es könnte möglich sein, verwenden sie das Symbol aus dem Argument übergeben, so dass ich modifiziert es auf die folgenden, die nicht mehr funktioniert, aber ich konnte nicht Figur heraus, warum:

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

damit, dass wir nicht sagen, verwenden sollten Ein Symbol, das auf eine lexikalisch gebundene Variable in a verweist Schließung wie oben, da die Namen von ihnen bei der Bewertung sind nicht garantiert, dass sie die gleichen sind wie sie in der Quelle geschrieben sind?

+0

Lexikalische Variablen sollen einfach nicht von außerhalb des Verschlusses zugänglich sein. Das ist mehr oder weniger der Sinn von ihnen. Wenn Sie auf diese Variablen zugreifen müssen, dann war die lexikalische Bindung in erster Linie die falsche Wahl, und ich glaube nicht, dass es eine gute Idee ist, nach Wegen zu suchen. – phils

Antwort

2

Sie haben ein paar Dinge hier falsch verstanden, aber der Schlüssel ist, dass das var1 Symbol, das Sie mit (funcall closure 'var1) vorbei sind, ist nicht das Symbol in der lexikalischen Umgebung, in der Sie Ihre Lambda-Funktion definiert.

Makro-Erweiterung der lexikalisch-let Form wird helfen, zu klären. Dies:

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

entlang dieser Linien erweitert: Welche

(progn 
    (defvar --cl-var1--) 
    (let ((--cl-var1-- 123)) 
    #'(lambda (name) 
     (message-box (format "%s" (symbol-value name)))))) 

ist zu sagen, dass die lexical-let Makro Wieder schreibt die Symbolnamen Sie in den Bindungen in einem nicht-Konflikt Weise angeben.

Beachten Sie, dass Sie nicht tatsächlich getan irgendetwas mit dieser var1 Bindung haben. Wenn Sie dies getan hätten, würden wir im Code zusätzliche Verweise auf --cl-var1-- sehen.

Wenn Sie das Symbol var1 diese Funktion übergeben, Sie übergeben die kanonischevar1, nicht --cl-var1-- (oder was auch immer es endete in der Praxis ist).

Das ist alles so, wie es sein sollte. Die Art der lexikalischen Bindung besteht darin, dass sie den in diesem Bereich geschriebenen Code beeinflusst, und nicht den Code außerhalb betrifft. Die (let ((closure (make-closure))) (funcall closure 'var1)) Form ist außerhalb, und daher sieht die lexikalisch gebunden var1 überhaupt nicht.


Wenn es um den Code „Korrektur“, bin ich eher kämpfen, um herauszufinden, wo Sie mit diesem gehen versuchen, aber mit meiner Interpretation würden Sie nicht einen Verschluss überhaupt wollen, weil Sie suche eher nach dynamischer Bindung als nach lexikalischer Bindung. z.B .:

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

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

auf die Frage nach dem Bearbeiten Basierend, würde ich vorschlagen, den Code leicht überarbeitet, so dass Sie nicht lexikalisch verwenden für die Werte Bindung Sie versuchen, die Funktionsargument übereinstimmen. Zum Beispiel:

(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 für den letzten Absatz und endgültigen Code, und für die Erwähnung, dass die Frage ist nicht so klar, was Absicht ist. – Drew

1

Lexikalisch-gebundene Variablen haben grundsätzlich keinen Namen (das heißt ihr Name ist nur eine temporäre Artefakt in dem Quellcode, aber nicht anwesend bei der Auswertung).

Sie können stattdessen eine Referenz auf die Variable verwenden:

;; -*- 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)))) 

Aber beachten Sie, dass ich die let Bindung von var1 zu bewegen hatte, denn sonst kann ich keine ref von außen bekommen.

Eine weitere Option ist für Sie manuell einen Namen auf Ihre lexikalischen Variablen geben:

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

Bitte beachte, dass ich var1 für zwei verschiedene Zwecke verwendet: einmal ist es der Name einer lexikalischen var, und das andere Mal ist es nur ein Symbol verwendet, um auszuwählen, welche var zu verwenden und die pcase thingy übersetzt man zum anderen: wir könnten "jeder" andere Name für die lexikalisch gebundene var verwenden und der Code würde genauso gut funktionieren (ohne den externen Aufrufer zu ändern) .

Verwandte Themen