2017-02-12 7 views
0

Ich versuche, eine Liste von Callback-Funktionen zu machen, die wie folgt aussehen könnte:eine Funktion in einer Liste registriert, wie es definiert wird

(("command1" . 'callback1) 
("command2" . 'callback2) 
    etc) 

Ich mag es würde, wenn ich könnte etwas tun könnte wie :

(define-callback callback1 "command1" args 
    (whatever the function does)) 

Statt

(defun callback1 (args) 
    (whatever the function does)) 

(add-to-list 'callback-info ("command1" . 'callback1)) 

gibt es eine bequeme Möglichkeit, dies zu tun, zum Beispiel mit Makros?

+0

Angegebene Symbole in Daten Listen/Conses machen keinen Sinn. '(foo 'bar)' <- warum würdest du 'bar' zitieren? Wenn es sich bei Listen um Daten handelt, müssen Sie möglicherweise die Liste angeben, aber nicht den Inhalt, um eine Auswertung zu verhindern. –

+0

Ja, das Zitat-Symbol sollte außerhalb der gesamten Liste gehen; Die Liste wird jedoch nicht wirklich im Quellcode angezeigt. –

Antwort

1

Diese Listen heißen Assoziationslisten in Lisp.

CL-USER 120 > (defvar *foo* '(("c1" . c1) ("c2" . c2))) 
*FOO* 

CL-USER 121 > (setf *foo* (acons "c0" `c1 *foo*)) 
(("c0" . C1) ("c1" . C1) ("c2" . C2)) 

CL-USER 122 > (assoc "c1" *foo* :test #'equal) 
("c1" . C1) 

Sie können Makros dafür schreiben, aber warum? Makros sind fortgeschrittenes Lisp und Sie möchten vielleicht zuerst die Grundlagen richtig machen.

Einige Probleme mit Ihnen Beispiel, das Sie vielleicht prüfen wollen:

  • was sind Assoc Listen?
  • Was sind nützliche Schlüsseltypen in Assoziationslisten?
  • warum Sie nicht brauchen Symbole in Daten zu zitieren listet
  • Variablen
  • Datenlisten für Rückrufe ohne Makros werden nicht zitiert müssen zitierte

Sie können genauso einfach erstellen solche Listen. Wir können eine Funktion create-callback vorstellen, die wie folgt verwendet werden würde:

(create-callback 'callback1 "command1" 
    (lambda (arg) 
    (whatever the function does))) 

Nun, warum sollten Sie einen Makro statt einer einfachen Funktion nutzen?

2

Dies ist ein gutes Beispiel für einen Ort, an dem es sich anbietet, einen Zwei-Ebenen-Ansatz zu verwenden, mit einer expliziten, funktionsbasierten Ebene und dann mit einer noch schöneren Makro-Ebene.

Beachten Sie folgendes Listing Common Lisp: Es sieht nur möglich aus Ihrer Frage, die Sie über elisp fragen, in diesem Fall kann so etwas gemacht werden, um zu arbeiten, aber es ist alles viel schmerzhafter.

Zunächst einmal werden wir Rückrufe in einem *callbacks* genannt alist halten:

(defvar *callbacks* '()) 

Hier ist eine Funktion, die den alist von Rückrufen

(defun initialize-callbacks() 
    (setf *callbacks* '()) 
    (values) 

Hier ist die Funktion, die einen Rückruf installiert löscht . Dazu wird in der Liste nach einem Rückruf mit dem angegebenen Namen gesucht, und wenn er dann ersetzt wird und andernfalls ein neuer Name erstellt wird. Wie bei allen Funktionen in der Funktionsschicht können wir die Testfunktion angeben, die uns darüber informiert, ob zwei Callback-Namen identisch sind: Standardmäßig ist dies #'eql, was für Symbole und Zahlen, nicht aber für Strings funktioniert. Symbole sind wahrscheinlich die bessere Wahl für die Namen von Callbacks als Strings, aber wir werden das unten behandeln.

(defun install-callback (name function &key (test #'eql)) 
    (let ((found (assoc name *callbacks* :test test))) 
    (if found 
     (setf (cdr found) function) 
     (push (cons name function) *callbacks*))) 
    name) 

Hier ist eine Funktion einen Rückruf zu finden, die die Funktion Objekt zurückgeben oder nil, wenn kein Rückruf mit diesem Namen ist.

(defun find-callback (name &key (test #'eql)) 
    (cdr (assoc name *callbacks* :test test))) 

Und eine Funktion, um einen benannten Rückruf zu entfernen. Das sagt dir nicht, ob es etwas getan hat: vielleicht sollte es.

(defun remove-callback (name &key (test #'eql)) 
    (setf *callbacks* (delete name *callbacks* :key #'car :test test)) 
    name) 

Jetzt kommt die Makroebene. Die Syntax davon wird (define-callback name arguments ...) sein, also sieht es ein bisschen wie eine Funktionsdefinition aus.

Es gibt drei Dinge über dieses Makro zu wissen.

  1. Es ist ein bisschen clever: weil man bei Makro-Expansionszeit, welche Art von Sache des Name des Callback wissen kann, ist, Sie dann entscheiden können, und es welchen Test zu verwenden, wenn die Callback-Installation, und es macht dies. Wenn der Name ein Symbol ist, wird auch ein block mit dem Symbol um den Körper der Funktionsdefinition umbrochen, so dass es etwas mehr nach einer durch defun definierten Funktion riecht: insbesondere können Sie return-from im Körper verwenden. Dies geschieht nicht, wenn der Name kein Symbol ist.

  2. Es ist nicht ganz clever genug: vor allem geht es nicht mit Docstrings in irgendeiner nützlichen Weise (es sollte sie aus dem Block ziehen, denke ich). Ich bin mir nicht sicher, ob das wichtig ist.

  3. Der Schalter, um den Test zu entscheiden, verwendet Ausdrücke wie '#'eql, die als (quote (function eql)) liest: das ist Verdrahtung in Funktionen in der Erweiterung zu vermeiden, da Funktionen nicht extern in CL Objekte sind. Aber ich bin mir nicht sicher, ob ich das richtig verstanden habe: Ich denke, was es gibt ist sicher, aber es wird möglicherweise nicht benötigt.

So, hier ist es

(defmacro define-callback (name arguments &body body) 
    `(install-callback ',name 
        ,(if (symbolp name) 
          `(lambda ,arguments 
          (block ,name 
           ,@body)) 
         `(lambda ,arguments 
          ,@body)) 
        :test ,(typecase name 
           (string '#'string=) 
           (symbol '#'eql) 
           (number '#'=) 
           (t '#'equal)))) 

Und schließlich sind hier zwei verschiedene Rückrufe definiert ist:

(define-callback "foo" (x) 
    (+ x 3)) 

(define-callback foo (x) 
    (return-from foo (+ x 1))) 
+0

Vielen Dank - ich wusste irgendwie, dass ein zweischichtiger Ansatz am besten wäre. Am Ende kam ich auf etwas wie: (Ich kann nicht herausfinden, wie man zurückquotes in Kommentare, siehe unten) –

0

Am Ende oben von den Responder unterstützt, habe ich es nach unten zu etwas wie:

(defmacro mk-make-command (name &rest body) 
    (let ((func-sym (intern (format "mk-cmd-%s" name)))) 
    (mk-register-command name func-sym) 
    `(defun ,func-sym (args &rest rest) 
     (progn 
     ,@body)))) 
Verwandte Themen