2010-09-08 10 views

Respuesta

52

defn define una función, defmacro define una macro.

La diferencia entre funciones y macros es que en una llamada de función primero se evalúan los argumentos de la función y luego el cuerpo de la función se evalúa utilizando los argumentos.

Las macros, por otro lado, describen una transformación de un código a otro. Cualquier evaluación tiene lugar después de la transformación.

Esto significa que los argumentos pueden evaluarse varias veces o no evaluarse en absoluto. Como ejemplo, or es una macro. Si el primer argumento de or es falso, el segundo argumento nunca será evaluado. Si or fuera una función, esto no sería posible, porque los argumentos siempre se evaluarían antes de que se ejecute la función.

Otra consecuencia de esto es que los argumentos de una macro no necesitan ser una expresión válida antes de expandir la macro. Por ejemplo, podría definir una macro mymacro tal que (mymacro (12 23 +)) se expande a (+ 23 12), por lo que esto funcionará aunque (12 23 +) por sí solo no tendría sentido. No podría hacer eso con una función porque (12 23 +) se evaluaría y provocaría un error antes de que se ejecute la función.

Un pequeño ejemplo para ilustrar la diferencia:

(defmacro twice [e] `(do ~e ~e)) 
(twice (println "foo")) 

La macro twice obtiene la lista (println "foo") como un argumento. Luego lo transforma en la lista (do (println "foo") (println "foo")). Este nuevo código es lo que se ejecuta.

(defn twice [e] `(do ~e ~e)) 
(twice (println "foo")) 

Aquí se println "foo" evaluted de inmediato. Como println devuelve nil, se llama dos veces con nil como argumento. twice ahora produce la lista (do nil nil) y la devuelve como resultado. Tenga en cuenta que aquí (do nil nil) no se evalúa como código, simplemente se trata como una lista.

+0

¿qué es lo que quiere decir con "argumentos pueden ser evaluados varias veces" cuando se refiere a las macros? ¿Puedes dar un ejemplo o explicar más? – Belun

+1

No creo que nadie pueda hacer esto, pero imagine una macro forloop. Puede invocarlo como (para bucle [x 0] (

+2

@Belun: Ejemplo más simple: (defmacro dos veces [e] '(do ~ e ~ e)) (dos veces (println" foo ")) se imprimirá foo dos veces. – sepp2k

0

defn definen una función y defmacro definen una macro.
La macro es como una función pero trata su argumento (si son expresiones como datos), luego los procesa y devuelve datos (lista de símbolos que son el código) y luego evalúa ese código de retorno. Entonces es reemplazar un código por otro (en tiempo de compilación).

3

Sin sonar sarcástico, se crea una función, mientras que el otro crea una macro . En Common Lisp (y supondré que esto también se aplica a clojure), las macros se expanden antes de la compilación real de funciones.Así, perezoso-cat:

(defmacro lazy-cat 
    "Expands to code which yields a lazy sequence of the concatenation 
    of the supplied colls. Each coll expr is not evaluated until it is 
    needed. 

    (lazy-cat xs ys zs) === (concat (lazy-seq xs) (lazy-seq ys) (lazy-seq zs))" 
    {:added "1.0"} 
    [& colls] 
    `(concat [email protected](map #(list `lazy-seq %) colls))) 

realmente va a ser ampliado a

`(concat [email protected](map #(list `lazy-seq %) colls))) 

de los cuales lazy-seq luego se ampliará a

(list 'new 'clojure.lang.LazySeq (list* '^{:once true} fn* [] body))) 

todos ante la realidad de procesamiento de los datos pasados para ellos

Hay una historia muy linda que ayuda a explicar la diferencia (seguido de algunos ejemplos informativos) a Practical Common Lisp: Chapter 8

10

Una macro es como tener un programador aprendiz que se puede escribir notas a:

A veces, si estoy tratando de depurar algo, me gusta cambiar algo así como

(* 3 2) 

en algo como esto:

(let [a (* 3 2)] (println "dbg: (* 3 2) = " a) a) 

Que funciona de la misma manera, excepto que imprime la expresión que acaba de evaluar, y su valor, y también devuelve el valor como resultado de la expresión completa . Esto significa que puedo dejar mi código sin perturbaciones mientras examino los valores intermedios.

Esto puede ser muy útil, pero lleva mucho tiempo y es propenso a escribir. ¡Puede imaginar delegando tales tareas a su aprendiz!

En lugar de contratar un aprendiz, puede programar el compilador para que haga estas cosas por usted.

;;debugging parts of expressions 
(defmacro dbg[x] `(let [x# ~x] (println "dbg:" '~x "=" x#) x#)) 

Ahora trata de:

(* 4 (dbg (* 3 2))) 

En realidad, hace que la transformación textual sobre el código para usted, aunque ser un ordenador, se escoge nombres ilegibles para sus variables en lugar de la "a" lo haría has elegido.

Podemos preguntarle qué haría por una expresión dada:

(macroexpand '(dbg (* 3 2))) 

Y esta es su respuesta, por lo que puede ver que realmente está reescribiendo el código para usted:

(let* [x__1698__auto__ (* 3 2)] 
     (clojure.core/println "dbg:" (quote (* 3 2)) "=" x__1698__auto__) 
     x__1698__auto__) 

Trate de escribir una función dbgf que haga lo mismo, y tendrá problemas, porque (dbgf (* 3 2)) -> (dbgf 6) antes de llamar a dbgf, y lo que dbgf haga, no puede recuperar la expresión que necesita para imprimir.

Estoy seguro de que se pueden pensar de varias maneras al respecto, como la evaluación en tiempo de ejecución o el envío de una cadena. Intenta escribir dbg usando defn en lugar de defmacro. Será una buena forma de convencerse a sí mismo de que las macros son buenas cosas para tener en un idioma.Una vez que tenga que trabajar, tratar de usarlo en una expresión que tiene un efecto secundario, así como un valor, como

(dbg (print "hi")) 

De hecho macros son tan bueno tener que estamos preparados para vivir con el (brackety ((sintaxis))) de los LISP para obtenerlos. (Aunque debo decir que me gusta bastante por sí mismo (pero luego (soy) un poco raro (en la cabeza)).

C también tiene macros, que funcionan aproximadamente de la misma manera, pero 'Siempre está yendo mal, y para hacerlo bien necesitas poner tantos corchetes en tu programa que parece LISP!

En realidad, se recomienda no utilizar las macros de C porque son muy propensas a errores, aunque Los he visto con gran efecto por personas que realmente sabían lo que estaban haciendo.

Las macros LISP son tan efectivas que el lenguaje en sí se construye a partir de ellas, como notará si mira los archivos fuente Clojure que están escritos en Clo Jure.

El lenguaje base es muy simple, por lo que es fácil de implementar, y luego la compleja superestructura se construye usando macros.

Espero que esto ayude. Es bastante más larga que mis respuestas habituales, porque has hecho una pregunta profunda. Buena suerte.

+7

Las macros C son _no_ como las macros Lisp. Las macros C operan en el _text_, es decir, en la cadena de caracteres que compone su código fuente, mientras que las macros Lisp operan en el _abstract syntax tree_, es decir, la estructura del programa. – Svante

+0

Buen punto, gracias. Se agregó la palabra "aproximadamente". –

+1

su código de ejemplo es un poco demasiado para un novato. intente explicar las cosas adicionales en sus ejemplos: x #, ~ x, '~ x. intenta explicar tu forma de pensar que te llevó a la solución, de esta manera un novato como yo puede conseguirte :) – Belun

34

Las otras respuestas lo cubren a fondo así que trataré de cubrirlo de la manera más sucinta posible. Le agradecería ediciones/comentarios sobre la forma de escribir de forma más sucinta, manteniendo en claro:

  • una funcióntransforma valores en otros valores.
    (reduce + (map inc [1 2 3])) => 9

  • un macro transforma código en otro código.
    (-> x a b c) => (c (b (a x))))

+0

el primer ejemplo es demasiado simple, el segundo ejemplo es demasiado complicado: D – Belun

+1

bien, hice el primero más complicado para equilibrar las cosas :) :) –

+1

e hizo el ejemplo de macro más simple –

Cuestiones relacionadas