Frage

Ich habe versucht, aus semantischen Gründen ein Lisp-Makro zu schreiben, das in anderen Programmiersprachen das Äquivalent von ++ ausführt.Ich habe versucht, dies auf verschiedene Weise zu erreichen, aber keine davon scheint zu funktionieren, und alle werden vom Interpreter akzeptiert, sodass ich nicht weiß, ob ich die richtige Syntax habe oder nicht.Meine Vorstellung davon, wie dies definiert werden würde, wäre

(defmacro ++ (variable)
  (incf variable))

aber das gibt mir einen SIMPLE-TYPE-ERROR, wenn ich versuche, es zu verwenden.Was würde dazu führen, dass es funktioniert?

War es hilfreich?

Lösung

Denken Sie daran, dass ein Makro einen Ausdruck ausgewertet werden zurückgibt. Um dies zu tun, müssen Sie Backquote:

(defmacro ++ (variable)
   `(incf ,variable))

Andere Tipps

Beide der vorherigen Antworten arbeiten, aber sie geben Sie ein Makro, das Sie als

nennen
(++ varname)

anstelle von varname ++ oder ++ varname, die ich vermute, Sie wollen. Ich weiß nicht, ob Sie tatsächlich die ehemaligen bekommen können, aber für die letztere, können Sie eine Lese Makro tun. Da es zwei Zeichen ist, ist ein Versand Makro wahrscheinlich am besten. Ungeprüfte, da ich nicht ein handliches Lauf Lisp, aber so etwas wie:

(defun plusplus-reader (stream subchar arg)
   (declare (ignore subchar arg))
   (list 'incf (read stream t nil t)))
(set-dispatch-macro-character #\+ #\+ #'plusplus-reader)

sollte ++ var eigentlich lesen Sie machen als (incf var).

Ich würde dringend davon abraten, einen Aliasnamen für incf machen. Es wäre zu reduzieren Lesbarkeit für alle anderen Code zu lesen, die sich fragen müssen: „Was ist das? Wie unterscheidet es sich von incf?“

Wenn Sie eine einfache Nachinkrement wollen, versuchen Sie dies:

(defmacro post-inc (number &optional (delta 1))
  "Returns the current value of number, and afterwards increases it by delta (default 1)."
  (let ((value (gensym)))
    `(let ((,value ,number))
       (incf ,number ,delta)
       ,value)))

Die Syntax (++ a) ist ein nutzloser Alias ​​für (incf a).Angenommen, Sie möchten die Semantik von Post-Inkrementieren:den alten Wert abrufen.In Common Lisp geschieht dies mit prog1, wie in: (prog1 i (incf i)).Common Lisp leidet nicht unter unzuverlässigen oder mehrdeutigen Auswertungsanweisungen.Der vorangehende Ausdruck bedeutet das i ausgewertet und der Wert dann irgendwo gespeichert (incf i) wird ausgewertet und dann der versteckte Wert zurückgegeben.

Damit ist es absolut kugelsicher pincf (Post-incf) ist nicht ganz trivial. (incf i) hat das schöne Anwesen, das i wird nur einmal ausgewertet.Wir würden gern (pincf i) diese Eigenschaft auch zu haben.Und so greift das einfache Makro zu kurz:

(defmacro pincf (place &optional (increment 1))
  `(prog1 ,place (incf ,place ,increment))

Um dies richtig zu machen, müssen wir auf den „Zuweisungsplatzanalysator“ von Lisp zurückgreifen get-setf-expansion um Materialien zu erhalten, die es unserem Makro ermöglichen, den Zugriff ordnungsgemäß zu kompilieren:

(defmacro pincf (place-expression &optional (increment 1) &environment env)
  (multiple-value-bind (temp-syms val-forms
                        store-vars store-form access-form)
                        (get-setf-expansion place-expression env)
    (when (cdr store-vars)
      (error "pincf: sorry, cannot increment multiple-value place. extend me!"))
    `(multiple-value-bind (,@temp-syms) (values ,@val-forms)
       (let ((,(car store-vars) ,access-form))
         (prog1 ,(car store-vars)
                (incf ,(car store-vars) ,increment)
                ,store-form)))))

Ein paar Tests mit CLISP.(Notiz:Erweiterungen, die auf Materialien von basieren get-setf-expansion kann umsetzungsspezifischen Code enthalten.Das bedeutet nicht, dass unser Makro nicht portierbar ist!)

8]> (macroexpand `(pincf simple))
(LET* ((#:VALUES-12672 (MULTIPLE-VALUE-LIST (VALUES))))
 (LET ((#:NEW-12671 SIMPLE))
  (PROG1 #:NEW-12671 (INCF #:NEW-12671 1) (SETQ SIMPLE #:NEW-12671)))) ;
T
[9]> (macroexpand `(pincf (fifth list)))
(LET*
 ((#:VALUES-12675 (MULTIPLE-VALUE-LIST (VALUES LIST)))
  (#:G12673 (POP #:VALUES-12675)))
 (LET ((#:G12674 (FIFTH #:G12673)))
  (PROG1 #:G12674 (INCF #:G12674 1)
   (SYSTEM::%RPLACA (CDDDDR #:G12673) #:G12674)))) ;
T
[10]> (macroexpand `(pincf (aref a 42)))
(LET*
 ((#:VALUES-12679 (MULTIPLE-VALUE-LIST (VALUES A 42)))
  (#:G12676 (POP #:VALUES-12679)) (#:G12677 (POP #:VALUES-12679)))
 (LET ((#:G12678 (AREF #:G12676 #:G12677)))
  (PROG1 #:G12678 (INCF #:G12678 1)
   (SYSTEM::STORE #:G12676 #:G12677 #:G12678)))) ;
T

Hier ist ein wichtiger Testfall.Hier enthält der Ort einen Nebeneffekt: (aref a (incf i)).Dies muss genau einmal ausgewertet werden!

[11]> (macroexpand `(pincf (aref a (incf i))))
(LET*
 ((#:VALUES-12683 (MULTIPLE-VALUE-LIST (VALUES A (INCF I))))
  (#:G12680 (POP #:VALUES-12683)) (#:G12681 (POP #:VALUES-12683)))
 (LET ((#:G12682 (AREF #:G12680 #:G12681)))
  (PROG1 #:G12682 (INCF #:G12682 1)
   (SYSTEM::STORE #:G12680 #:G12681 #:G12682)))) ;
T

Was also zuerst passiert, ist Folgendes A Und (INCF I) werden ausgewertet und zu temporären Variablen #:G12680 Und #:G12681.Auf das Array wird zugegriffen und der Wert wird darin erfasst #:G12682.Dann haben wir unser PROG1 das diesen Wert für die Rückgabe behält.Der Wert wird erhöht und über CLISPs wieder im Array-Speicherort gespeichert system::store Funktion.Beachten Sie, dass dieser Store-Aufruf die temporären Variablen und nicht die ursprünglichen Ausdrücke verwendet A Und I. (INCF I) kommt nur einmal vor.

Semantisch, die Präfix-Operatoren ++ und - in einer Sprache wie C ++ oder was auch immer sind äquivalent incf / DECF in Common Lisp. Wenn Sie dies und erkennen, wie Ihr (falsch) Makro, suchen tatsächlich für eine syntaktische Änderung dann haben Sie bereits gezeigt, wie es mit Backticks zu tun wie `(incf, x). Sie haben sogar gezeigt worden, wie der Leser um hacken, um etwas näher an nicht-Lisp-Syntax zu erhalten. Das ist der Knackpunkt aber, wie keines dieser Dinge ist eine gute Idee. Im Allgemeinen nicht idiomatische Codierung eine Sprache ähnelt andere enger macht entpuppt einfach nicht so eine gute Idee zu sein.

Wenn jedoch tatsächlich für die Semantik sucht, haben Sie bereits die Vorsilbe Versionen wie erwähnt, aber die Postfix-Versionen gehen, um einfach zu syntaktisch nicht übereinstimmen. Man könnte es mit genügend Leser hackery tun, aber es wäre nicht schön sein.

Wenn das, was Sie suchen, ich würde vorschlagen, dass a) halten mit incf / DECF Namen, da sie idiomatische sind und gut funktionieren und b) schreiben post-incf, post-DECF Versionen, zB (defmacro post-incf (x) `(prog1, x (incf, x)) Arten von Dingen.

Ich persönlich sehe nicht, wie dies besonders nützlich sein würde, aber ymmv.

Für Prä-Inkrement, gibt es bereits incf, aber Sie können Ihre eigene mit

definieren
(define-modify-macro my-incf () 1+)

Für Post-Inkrement, können Sie diese (von Tarif-utils) verwenden:

(defmacro define-values-post-modify-macro (name val-vars lambda-list function)
 "Multiple-values variant on define-modify macro, to yield pre-modification values"
 (let ((env (gensym "ENV")))
   `(defmacro ,name (,@val-vars ,@lambda-list &environment ,env)
      (multiple-value-bind (vars vals store-vars writer-form reader-form)
          (get-setf-expansion `(values ,,@val-vars) ,env)
       (let ((val-temps (mapcar #'(lambda (temp) (gensym (symbol-name temp)))
                                 ',val-vars)))
          `(let* (,@(mapcar #'list vars vals)
                  ,@store-vars)
             (multiple-value-bind ,val-temps ,reader-form
               (multiple-value-setq ,store-vars
                 (,',function ,@val-temps ,,@lambda-list))
               ,writer-form
               (values ,@val-temps))))))))

(defmacro define-post-modify-macro (name lambda-list function)
 "Variant on define-modify-macro, to yield pre-modification values"
 `(define-values-post-modify-macro ,name (,(gensym)) ,lambda-list ,function))

(define-post-modify-macro post-incf () 1+)

Altough ich auf jeden Fall im Auge, die Bemerkungen und Heads-up halten würde, dass simon Kommentare in seinem Beitrag, ich glaube wirklich, dass user10029 's Ansatz noch ist ein Versuch wert , so, nur zum Spaß, habe ich versucht, es mit der akzeptierten Antwort zu kombinieren, um die ++ x Operator Arbeit zu machen (das heißt, in 1 den Wert von x erhöht). Probieren Sie es aus!

Erklärung : Der gute alte SBCL würde seine Version nicht kompilieren, weil das Symbol ‚+‘ muss ausdrücklich auf die mit make-dispatch-macro-character dispatch-char-Lookup-Tabelle gesetzt werden, und das Makro benötigt wird, noch zu übergehen der Name der variablen, bevor es zu bewerten. So soll dies die Arbeit machen:

(defmacro increment (variable)
  "The accepted answer"
  `(incf ,variable))

(make-dispatch-macro-character #\+) ; make the dispatcher grab '+'

(defun |inc-reader| (stream subchar arg)
  "sets ++<NUM> as an alias for (incf <NUM>).
   Example: (setf x 1233.56) =>1233.56
            ++x => 1234.56
            x => 1234.56"
   (declare (ignore subchar arg))
   (list 'increment (read stream t nil t)))

(set-dispatch-macro-character #\+ #\+ #'|inc-reader|)

Siehe |inc-reader| des docstring für ein Anwendungsbeispiel. Die (eng) bezogene Dokumentation finden Sie hier:

Diese Implementierung hat zur Folge, dass die Anzahl Einträge wie +123 nicht mehr (der Debugger mit no dispatch function defined for #\Newline springt in) verstanden werden, sondern weitere Abhilfe (oder Vermeidung von selbst) erscheint sinnvoll: Wenn Sie immer noch mit diesem halten wollen, vielleicht die beste Wahl nicht nehmen ++ als Präfix, sondern ## oder jede andere mehr DSL-ish Lösung

Prost!

Andres

Das sollte es tun, aber ich bin kein Lisp-Guru.

(defmacro ++ (variable)
  `(setq ,variable (+ ,variable 1)))
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top