2012-02-23 12 views
9

A livello concettuale una macro in LISP (e dialetti) prendere un pezzo di codice (come lista) e restituisce un altro pezzo di codice (di nuovo come lista).differenza tra l'utilizzo lista e segno di spunta di nuovo nelle macro

Sulla base di principio di sopra di una macro semplice potrebbe essere:

(defmacro zz [a] (list print a)) 
;macroexpand says : (#<core$print [email protected]> "Hello") 

Ma in clojure questo può anche essere scritta come:

(defmacro zz [a] `(print ~a)) 
;macroexpand says : (clojure.core/print "Hello") 

Io non sono esattamente sicuro circa la differenza qui e che dovrebbe essere il modo preferito Il primo sembra semplice, visto che dovremmo restituire la lista ed evitare l'uso di caratteri strani come back tick.

risposta

8

Nessuno ha fatto notare ancora ... la differenza tra le 2 macro è questo: la tua seconda forma (usando backtick)

(defmacro zz [a] `(print ~a)) 

è equivalente a:

(defmacro zz [a] (list 'print a)) 

che è diverso dal vostro primo esempio:

(defmacro zz [a] (list print a)) 

Nota la quota singola mancante: ecco perché il tuo macroexpand è diverso. Sono d'accordo con le altre persone della pubblicazione: usando backquote è più convenzionale se la macro ha una 'forma' abbastanza semplice. Se avete a che fare codice a piedi o in costruzione dinamica (vale a dire una macro complessa), quindi utilizzando gli elenchi e la costruzione in su è spesso ciò che è fatto.

Spero che questa spiegazione ha un senso.

+1

In realtà è equivalente a '(list \' print a) ', che è leggermente diverso:' 'print' funziona solo se il chiamante ha 'print' che si riferisce a' clojure.core/print' piuttosto che a un locale, o nessun legame (diciamo che l'hanno escluso dal ': refer-clojure' del loro spazio dei nomi). '\' print', d'altra parte, si espande direttamente a 'clojure.core/print', quindi non è necessario e corretto in qualsiasi contesto. – amalloy

+0

@amalloy Grazie per aver risposto, in base alla tua congestione ho appena provato a fare un macroexpand (via slime) su '' '(print ~ a)' e sono tornato: '(seq (concat (lista 'print) (lista a))) '- ah, ma stai dicendo che la forma pre-trasformata è più simile ad avere' '' print' 'vero? –

+0

Non riesco a leggere la tua risposta molto bene perché SO sta mangiando alcuni dei caratteri retrospettivi, ma: Slime nasconde i namespace da te (e macroexpand non è necessario). Se si cita l'espressione, digitando ''\' (print ~ a) 'nel repl, si vede che è equivalente a' (clojure.core/seq (clojure.core/concat (clojure.core/list (quote clojure.core/print)) (clojure.core/list a))) '. Il 'clojure.core/print' è l'importante distinzione che stavo facendo. – amalloy

1

Nella mia esperienza sono equivalenti. Anche se ci possono essere alcuni casi limite di cui non sono a conoscenza.

esempio 's @islon può equivalentemente essere scritta come:

Per contrasto, il codice di cui sopra può essere scritto in modo equivalente in questo modo:

(defmacro unless [condition & body] 
    (list 'if (list 'not condition) 
      (list* 'do body))) 
5

C'è una differenza di stile tra di loro. Il tuo esempio è molto semplice ma in macro più complessi la differenza sarà maggiore.

Ad esempio, il meno che macro come definito in "The Joy of Clojure" libro:

(defmacro unless [condition & body] 
    `(if (not ~condition) 
     (do [email protected]))) 

Dal libro:

Syntax-citazione permette quanto segue se forma di agire come un una sorta di modello per l'espressione che qualsiasi uso della macro diventano quando si è espanso.

Quando si crea una macro, scegliere sempre lo stile più leggibile e più idiomatico.

Per contrastare, il codice di cui sopra può essere scritta in modo equivalente in questo modo:

(defmacro unless [condition & body] 
    (list 'if (list 'not condition) 
      (list* 'do body))) 
6

Costruire liste in modo esplicito è "più semplice", in un certo senso, perché ci sono alcuni concetti chiave che devi sapere: basta accettare una lista e cambiarla finché non si ha una nuova lista. Backtick è una comoda scorciatoia per "templare" blocchi di codice; è possibile scrivere qualsiasi macro senza di essa, ma per qualsiasi macro grande diventa rapidamente molto spiacevole. Per esempio, consideriamo due modi di scrivere let come una macro su fn:

(defmacro let [bindings & body] 
    (let [names (take-nth 2 bindings) 
     vals (take-nth 2 (rest bindings))] 
    `((fn [[email protected]] 
     (do [email protected])) 
     [email protected]))) 

(defmacro let [bindings & body] 
    (let [names (take-nth 2 bindings) 
     vals (take-nth 2 (rest bindings))] 
    (cons (list `fn (vec names) (cons `do body)) 
      vals))) 

Nel primo caso, utilizzando backtick rende abbastanza chiaro che si sta scrivendo una funzione dei nomi che contengono il corpo, e quindi chiamando con i valori - il codice macro è "modellato" come il codice di espansione, quindi puoi immaginare come sarà.

Nel secondo caso, con solo cons e list dappertutto, è un vero problema trovare l'aspetto che avrà l'espansione. Naturalmente non è sempre così: a volte può essere più chiaro scrivere qualcosa senza un apice.

Un altro punto molto importante è stato fatto da Kyle Burton: print non è lo stesso di 'print! L'espansione della macro deve contenere il simbolo print, non il suo valore (che è una funzione). Incorporare oggetti (come le funzioni) nel codice è molto fragile e funziona solo per sbaglio. Quindi assicurati che le macro si espandano al codice che potresti effettivamente aver scritto da te e che il sistema di valutazione faccia il duro lavoro - potresti digitare il simbolo print, ma non puoi digitare un puntatore al valore corrente della funzione print .

Problemi correlati