Pregunta

He estado tratando de escribir una macro Lisp que efectúan el equivalente a ++ en otros lenguajes de programación, por razones semánticas.Yo he intentado hacerlo de varias maneras diferentes, pero ninguno de ellos parece funcionar, y todos son aceptados por el intérprete, así que no sé si tengo la sintaxis correcta o no.Mi idea de cómo podría ser definido sería

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

pero esto me da un SIMPLE TIPO de ERROR al intentar utilizarla.¿Qué haría el trabajo?

¿Fue útil?

Solución

Recuerde que una macro devuelve una expresión a evaluar.Con el fin de hacer esto, usted tiene que backquote:

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

Otros consejos

Tanto de las respuestas anteriores trabajos, pero que te dan una macro que se llama como

(++ varname)

en lugar de varname++ o ++varname, que sospecho que usted desea.No sé si usted puede conseguir realmente el anterior, pero para el último, usted puede hacer una lectura de la macro.Dado que se trata de dos personajes, un despacho macro es probablemente el mejor.No ha sido probado, ya que no tengo a la mano que ejecuta lisp, pero algo como:

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

debe hacer ++var realidad leer como (incf var).

Yo les recomiendo vivamente en contra de hacer un alias para incf.Se podría reducir la legibilidad para alguien más la lectura de su código que hay que preguntarse "¿qué es esto?¿cómo es diferente de incf?"

Si desea que un simple post-incremento, intente esto:

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

La sintaxis (++ a) es un inútil alias para (incf a).Pero supongamos que usted desea que la semántica de post-incremento:recuperar el valor antiguo.En Common Lisp, esto se hace con prog1, como en: (prog1 i (incf i)).Common Lisp no sufre poco fiables o ambiguo en la evaluación de los pedidos.La expresión anterior significa que i se evalúa, y el valor está escondido en algún lugar, entonces (incf i) se evalúa y, a continuación, el escondido valor es devuelto.

Hacer una completamente a prueba de balas pincf (post-incf) no es del todo trivial. (incf i) tiene la propiedad de que i se evalúa sólo una vez.Nos gustaría (pincf i) también tienen esa propiedad.Y así, el simple macro se queda corto:

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

Para hacer este derecho, tenemos que recurrir a Lisp de la "asignación de lugar analizador" llamado get-setf-expansion para obtener los materiales que permiten que nuestra macro para compilar el acceso correctamente:

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

Un par de pruebas con CLISP.(Nota:las expansiones de depender de los materiales de get-setf-expansion puede contener específico de la implementación del código.Esto no significa que nuestro macro no es portátil!)

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

Ahora aquí es una de las claves del caso de prueba.Aquí, el lugar contiene un efecto secundario: (aref a (incf i)).Esto debe ser evaluado exactamente una vez!

[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

Así que lo que ocurra primero es que A y (INCF I) se evalúan, y se convierten en las variables temporales #:G12680 y #:G12681.La matriz se accede y el valor es capturado en #:G12682.Luego tenemos a nuestro PROG1 que conserva el valor para volver.El valor se incrementa, y se almacena de nuevo en la matriz de ubicación a través de CLISP del system::store la función.Tenga en cuenta que esta tienda llamada utiliza las variables temporales, no el original expresiones A y I. (INCF I) aparece sólo una vez.

Semánticamente, el prefijo de los operadores ++ y -- en un lenguaje como c++ o lo que sea son equivalentes incf/decf en common lisp.Si te das cuenta de esto y, como su (incorrecto) macro, realmente están buscando un cambio sintáctico entonces usted ya ha sido demostrado cómo hacerlo con comillas simples inclinadas me gusta " (incf ,x).Usted incluso ha sido demostrado cómo hacer que el lector hack de todo esto para obtener algo más cercano a la no-lisp sintaxis.Ese es el problema, aunque, como ninguna de estas cosas es una buena idea.En general, no idiomáticas de codificación para hacer que un idioma se asemejan a otra más estrechamente simplemente no resultó ser una buena idea.

Sin embargo, si realmente están buscando la semántica, ya tienes el prefijo versiones como se señaló, pero el postfix versiones no va a ser fácil para que coincida con la sintaxis.Podría hacerlo con suficiente lector trucos, pero no sería bastante.

Si eso es lo que estamos buscando, me gustaría sugerir a) palo con incf/decf nombres, ya que son idiomáticas y funcionan bien, y b) escribir el post-incf, post-decf versiones, e.g (defmacro post-incf (x) `(prog1 ,x (incf ,x)) tipo de cosas.

Personalmente, no veo cómo esto sería especialmente útil pero ymmv.

Para el pre-incremento, ya hay incf, pero usted puede definir su propia con

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

Para el post-incremento, podría utilizar este (de tarifa-utils):

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

Aunque definitivamente, me gustaría mantener en la mente de los comentarios y de heads-up que simon comenta en su post, de verdad creo que user10029el enfoque es todavía vale la pena intentarlo, así que, sólo por diversión, traté de combinar con la aceptación de respuesta para hacer el ++x de trabajo de un operador (es decir, incrementar el valor de x en 1).Darle una oportunidad!

Explicación:Bueno viejo SBCL no compilar su versión porque el símbolo '+' debe establecerse explícitamente en el despacho-char tabla de búsqueda con make-dispatch-macro-character, y la macro es todavía necesario para pasar sobre el nombre de la variable antes de evaluar.Por lo que este debe hacer el trabajo:

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

Ver |inc-reader|'s docstring para un ejemplo de uso.La (de cerca) la documentación relacionada se puede encontrar aquí:

Esta aplicación tiene como consecuencia que el número de entradas como +123 ya no se ven (el depurador de saltos con no dispatch function defined for #\Newline), pero aún más la solución (o incluso evitar) parece razonable:si usted todavía quiere seguir con esto, tal vez la mejor opción es no tomar ++ como prefijo, pero ## o de cualquier otro más DSL-ish solución

saludos!

Andres

Esto debe hacer el truco, pero yo no soy un lisp gurú.

(defmacro ++ (variable)
  `(setq ,variable (+ ,variable 1)))
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top