2017-06-15 6 views
0

Um ein Problem zu lösen, habe ich während der Verwendung von Common Lisp, habe ich das Netz durchforstet und gefunden: Variable references in lisp die sehr zu meinem Problem verwandt ist.Lisp/parameter-passing

Lesen dieses Teil:

funktionale Denken Sie! .................. Also, um zu tun, was Sie wollen, muss der Code a) im Bereich sein oder b) Zugriff auf Funktionen, die im Bereich sind .

Ich versuche, einen Sinn zu machen, aber die Dinge sind nicht klar.

Zuerst Ich bin nicht sicher, was a) und b) beziehen sich auf die im letzten Satz.

Zweitens, wie kann ich praktisch Verwendung der beiden Teile des Codes machen, vorausgesetzt, das gewünschte Ergebnis zu bekommen?

Vielen Dank im Voraus für jeden relevanten Tipp.

+2

Was ist das 'gewünschte Ergebnis'? In der Tat, welche Frage stellst du? – tfb

+0

Grundsätzlich können Sie Variablen, auf die Sie Zugriff haben, entweder direkt (den Bereich) oder indirekt festlegen, indem Sie eine Closure aufrufen, die eine Gruppe von Variablen schließt (oder ein Feld oder Array mutiert). Lesen Sie http://www.gigamonkeys.com/book/variables.html – coredump

Antwort

1

Ich kann immer noch nicht ganz herausfinden, was Fragen Sie fragen, aber ich glaube, Sie wollen wissen, wie Sie Bindungen in einem Bereich innerhalb von Funktionen ändern können, die die Bindungen nicht sehen können. Also hier ist eine Antwort auf diese Frage.

Die erste Sache zu verstehen ist, dass Umfang in modernen Programmiersprachen ist schrecklich einfach: Wenn Sie eine Bindung (eine Verbindung zwischen einem Namen und einem Wert) sehen können, dann haben Sie Zugriff darauf, und wenn es veränderbar ist, können Sie mutieren es. Vormoderne Programmiersprachen haben alle möglichen arkanen Regeln, die dies auf Grund der einfachen Implementierung auf kleinen Computern vor langer Zeit einschränken (wir sind alle verflucht von dem Vermächtnis der PDP-11), aber moderne fegen das alles weg. Common Lisp ist in diesem Sinne meist eine moderne Programmiersprache.

Also, was Sie tun müssen, ist eine Bindung irgendwie zu erfassen, und dann, dass die Bindung in gefangen passieren, was auch immer Funktionen, die Sie anrufen möchten, wo auf sie zugegriffen oder mutiert werden. Die Art und Weise, wie Sie eine Bindung erfassen, erfolgt mit einer Funktion.

So, hier ist ein einfaches Beispiel dafür, wie dies in CL zu tun:

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

In diesem Code die Funktion durch das erste Argument zu bar Zugriff hat geschaffen, um die Bindung von x und ist so geschrieben so dass das Aufrufen ohne Argumente den Wert der Bindung zurückgibt, während das Aufrufen mit einem Argument den Wert festlegt. Hier ist, dass in Aktion:

CL-USER 5 > (foo 1 2) 
initially 1 
then 2 
2 

So können Sie sehen, das funktioniert: die Bindung von x durch Aufrufe der Funktion modifiziert ist, die sie gefangen.

Aber das ist syntaktisch klobig: es wäre schön, wenn wir all diese expliziten funcall s und lambda s vermeiden können (wir erstere in einer Lisp-1 vermeiden könnten, aber es ist immer noch nicht wirklich schön). Also hier einige Code, der das tut (Erklärung davon unten):

(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, also das Makro capture sind nur syntaktischer Zucker, die eine Funktion identisch mit dem im ursprünglichen Code erstellt. Es muss ein Makro sein, da die Funktion im Bereich der Bindung erstellt werden muss, die es erfasst.

captured ist dann eine triviale Funktion, die nur die Funktion von capture in geeigneter Weise erstellt ruft: so anstatt zu sagen (funcall c) wir (captured c) sagen können.

Schließlich lehrt das defsetf Formular setf, wie erfasste Bindungen festgelegt werden, so dass (setf (captured x) y) funktioniert.

Und hier sind Neuimplementierungen der oben foo und bar Funktionen, die diese verwenden:

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

Ich denke, es ist offensichtlich, dass dies schöner ist die explizite funcall s zu lesen als alle und lambda oben s. Und es funktioniert auf die gleiche Art und Weise:

CL-USER 6 > (foo 1 2) 
initially 1 
then 2 
2 

Im Übrigen können Sie Expresssions erfassen, nicht nur Variablenbindungen, natürlich, so lange wie setf weiß, was mit ihnen zu tun (so lange, wie sie das, was sie sind Anrufe 'Orte'):

(defun fish (l) 
    (bone (capture (car l))) 
    l) 

(defun bone (c) 
    (setf (captured c) 'bone)) 

Und jetzt

CL-USER 13 > (fish (list 1 2)) 
(bone 2)