2014-10-05 8 views
7

C'è qualcosa Devo mancare sulla macro filettatura in Clojure.differenza dispari tra le funzioni denominate e anonimi quando si utilizza la filettatura macro

devo una mappa con i valori che sono le mappe come bene, e mi piacerebbe una ricerca nel risultato di un'altra ricerca. Lasciate che la mappa sia un semplice {:a {:b 2}} - prima voglio cercare la chiave :a, che sta per cedere {:b 2}, poi cercare b, il risultato è 2. La chiave per la seconda ricerca deve essere il risultato di una funzione.

((fn [x] (get x :b)) ({:a {:b 2} } :a)) 
=> 2 

Ok, cerchiamo di renderlo più leggibile con la macro filettatura.

(-> {:a {:b 2} } :a (fn [x] (get x :b))) 

Vale a dire applicare :a come una funzione sulla mappa, quindi applicare un'altra funzione. Beh, questo non funziona: CompilerException java.lang.IllegalArgumentException: Parameter declaration :a should be a vector

Stranamente, se la funzione anonima viene estratto a un nominato uno, allora funziona benissimo:

(defn f [x] (get x :b)) 
(-> {:a {:b 2} } :a f) 
=> 2 

O anche:

(def f (fn [x] (get x :b))) 
(-> {:a {:b 2} } :a f) 
=> 2 

Perché c'è una differenza tra il modo in cui funzionano le funzioni con nome e quelle anonime?

risposta

4

La macro filettatura vede, e altera, ciascuna sottomaschera nella serie prima forma viene valutata ricorsivamente inserendo la forma precedente come primo argomento a ciascuna sottomaschera.

si inizia con:

(-> {:a {:b 2} } :a (fn [x] (get x :b))) 

questo diventa:

(-> (:a {:a {:b 2}}) (fn [x] (get x :b))) 

questo diventa:

(fn (:a {:b {:b 2}}) [x] (get x :b))) 

che non è chiaramente quello che si voleva a tutti.

Ma vediamo che cosa succede se si aggiunge parentesi in più intorno alla funzione anonima:

(-> {:a {:b 2}} :a ((fn [x] (get x :b)))) 

(-> (:a {:a {:b 2}}) ((fn [x] (get x :b)))) 

(-> ((fn [x] (get x :b)) (:a {:a {:b 2}}))) 

((fn [x] (get x :b)) (:a {:a {:b 2}})) 

All'ultimo macroexpansion ricorsiva della forma -> stiamo lasciati con codice valido che fa quello che si vuole.

+0

Hmm, interessante, grazie. Ho bisogno di orientarmi sui macro: ovviamente "il primo argomento per ogni sottomaschera" non significa "prima argomentazione della funzione anonima" - è così che inizialmente pensavo. –

+0

sì, le macro sono molto letterale, quindi vede e altera non un 'fn', ma una chiamata a' fn'. – noisesmith

3

Per completare la risposta di noisesmith, in questo caso particolare non è necessario la macro filettatura. Il modo idiomatico per ottenere un valore da una mappa nidificata è get-in. es:

(get-in {:a {:b 2}} [:a :b]) 

=>

2 
+0

Ancora meglio :) Grazie. –