Die Syntax (++ a)
ein nutzloses Alias für (incf a)
ist aber, wenn man die Semantik Nachinkrement wollen. Den alten Wert abzurufen. In Common Lisp geschieht dies mit prog1
, wie in: (prog1 i (incf i))
. Common Lisp leidet nicht unter unzuverlässigen oder mehrdeutigen Evaluationsreihenfolgen.Der obige Ausdruck bedeutet, dass i
ausgewertet wird, und der Wert irgendwo gespeichert wird, dann (incf i)
ausgewertet wird, und dann wird der verdeckte Wert zurückgegeben
Ein vollständig kugelsicheres pincf
(post-incf
) ist nicht ganz trivial. (incf i)
hat die nette Eigenschaft, dass i
nur einmal ausgewertet wird. Wir möchten, dass (pincf i)
auch diese Eigenschaft hat. Und so ist die einfache Makro greift zu kurz:
(defmacro pincf (place &optional (increment 1))
`(prog1 ,place (incf ,place ,increment))
dieses Recht zu tun, wir riefen zu Lisp die „Zuordnung Ort Analysator“ get-setf-expansion
zurückgreifen müssen, um Materialien zu erhalten, die unsere erlauben Makro den Zugriff richtig 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. (Hinweis: Erweiterungen auf Materialien aus get-setf-expansion
verlassen Implementierung spezifische Code enthalten Dies bedeutet nicht, unsere Makro nicht tragbar 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 nun ein wichtiger Testfall ist. Hier enthält der Platz 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
Also, was zuerst passiert, ist, dass A
und (INCF I)
ausgewertet werden, und werden die temporären Variablen #:G12680
und #:G12681
. Auf das Array wird zugegriffen, und der Wert wird in #:G12682
erfasst. Dann haben wir unsere PROG1
, die diesen Wert für die Rückgabe behält. Der Wert wird erhöht und über die CLISP-Funktion system::store
in den Array-Speicherort zurückgespeichert. Beachten Sie, dass dieser Geschäftsaufruf die temporären Variablen verwendet und nicht die ursprünglichen Ausdrücke A
und I
. (INCF I)
erscheint nur einmal.
kein Duplikat, aber im Zusammenhang: [? Zerstörend Makro oder Funktion wie incf Schreiben] (http://stackoverflow.com/q/19485964/1281433) –