2016-01-27 7 views
5

Il seguente codice è dal capitolo 8.1.1 del (la seconda edizione di) The Joy of Clojure da Fogus, Houser:costruzione si toglie la quotatura in contestuale-eval nella gioia di Clojure

(defn contextual-eval [ctx expr] 
    (eval 
    `(let [[email protected](mapcat (fn [[k v]] [k `'~v]) ctx)] ; Build let bindings at compile time 
     ~expr))) 

(contextual-eval '{a 1, b 2} '(+ a b)) 
;;=> 3 
(contextual-eval '{a 1, b 2} '(let [b 1000] (+ a b))) 
;;=> 1001 

faccio Non capisco davvero il significato della costruzione `'~v. Qualcuno può approfondire su questo?

Nel libro, si dice soltanto che

Gli attacchi realizzati uso l'interessante ` '~ v modello di raccogliere il valore del costruito binding in fase di esecuzione.

Per esempio

(contextual-eval '{a 1, b 2} '(+ a b)) 

è espansa per

(let [a '1 b '2] (+ a b))) 

e non capisco il motivo per cui vengono introdotti quelle citazioni, che cosa sono buone per.

Inoltre, abbiamo il seguente comportamento:

(contextual-eval '{a 1, b (+ a 1)} '(+ a b)) 
ClassCastException clojure.lang.PersistentList cannot be cast to java.lang.Number 

(defn contextual-eval' [ctx expr] 
    (eval 
    `(let [[email protected](mapcat (fn [[k v]] [k v]) ctx)] 
     ~expr))) 

(contextual-eval' '{a 1, b (+ a 1)} '(+ a b)) 
;=> 3 

risposta

7

Quell'espressione utilizza quasi tutti i particolari simboli line-rumore dall'aspetto disponibile in Clojure, quindi vale la pena prendere lo distingue:

  • ` è un lettore-macro per "syntax-quote"
    "syntax-quote" è speciale tra le macro del lettore perché è possibile chiamare tale funzione solo tramite la sua forma breve. Ad esempio, non è possibile chiamare qualcosa come (syntax-quote something-here) anziché scrivere `something-here. Fornisce un ricco set di opzioni per specificare quali parti dell'espressione devono essere valutate e che dovrebbero essere prese alla lettera.
  • ' È una macro di lettore per il modulo speciale quote. Fa sì che l'espressione che racchiude non sia valutata, e invece che sia trattata come dati. Se si desidera scrivere un modulo letterale quote senza valutarlo, è possibile scrivere `'something per ottenere `(quote something) come risultato. E questo farebbe sì che l'espressione di virgoletta risultante non venga valutata, ma appena restituita come è senza eseguirla.

  • ~ è una parte della sintassi di sintassi-citazione (è "quote" con una sintassi) che significa "effettivamente lascia che questa parte venga eseguita" quindi se si dispone di una lista grande che si desidera prendere alla lettera (non eseguita correttamente ora), eccetto che hai un articolo che vuoi veramente valutare in questo momento, quindi potresti scrivere `(a b c ~d e f g) e d sarebbe l'unica cosa in quella lista che viene valutata a qualunque cosa sia attualmente definita essere.

Così ora siamo in grado di mettere tutto insieme:

`'~ significa "fare un'espressione citazione che contiene il valore di v come è adesso"

user> (def v 4) 
#'user/v 
user> `'~v 
(quote 4) 

E al motivazione per questa fantasia:

(contextual-eval '{a 1, b 2} '(+ a b)) 

sembra solo aggiungere qualche pensiero in più senza essere nefit perché fondamentalmente sta solo citando i valori 1 e 2. Siccome questi sono "valori" corretti, non cambiano comunque.

Ora, se l'espressione era invece:

(contextual-eval 
    '{a (slurp "https://example.com/launch?getCode") 
    b the-big-red-button} 
    '(press b a)) 

allora avrebbe più senso di essere attenti a quando quel particolare pezzo di codice viene eseguito. Quindi questo schema riguarda il controllo di quale fase della vita di un programma esegue effettivamente il codice. Clojure ha diverse "volte" quando il codice può essere eseguito:

  • in fase di valutazione della macro: durante la creazione del codice. (Gli effetti collaterali qui richiedono molta previdenza).
  • quando i tuoi spazi dei nomi si stanno caricando: questo è quando i moduli vengono eseguiti al livello più alto. Ciò accade spesso quando si avvia il programma e prima che venga invocato lo main.
  • cose che vengono eseguiti a seguito di esecuzione main

ps: le definizioni di cui sopra sono adattati al contesto di questa domanda e non lo scopo di utilizzare i termini "ufficiali".

+0

Grazie per la risposta. Ma quello che hai scritto è esattamente ciò che ho già capito;). Quello che non capisco è * perché * gli autori introducono che citano del tutto. Ad esempio, (contextual-eval '{a 1, b 2} '(+ a b)) viene espanso a (let [a '1 b '2] (+ a b)). A cosa servono queste citazioni ('1 e' 2)? –

+0

Grazie, lo aggiungerò alla mia risposta. –

+0

Dovrei aggiungere che questi moduli si presentano molto raramente nel normale codice del clojure, se mai lo fanno. È bello capirli e pensare attentamente a quello che stai facendo se ti ritrovi a usarli davvero. –