2014-10-05 24 views
7

Jest coś, czego nie mogę pominąć w makro wątku w Clojure.Nieparzysta różnica między nazwanymi i anonimowymi funkcjami podczas korzystania z makra gwintowania.

Mam mapę z wartościami, które są również mapami, i chciałbym sprawdzić w wyniku innego wyszukiwania. Niech mapa będzie prosta: {:a {:b 2}} - najpierw chcę wyszukać klucz :a, który da mu {:b 2}, a następnie przejdzie w górę b, wynikiem jest 2. Klucz do drugiego wyszukiwania musi być wynikiem funkcji.

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

Ok, zróbmy to bardziej czytelnym dzięki makrze do gwintowania.

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

tj. Zastosuj :a jako funkcję na mapie, a następnie zastosuj inną funkcję. Cóż, to nie działa: CompilerException java.lang.IllegalArgumentException: Parameter declaration :a should be a vector

Co dziwne, jeśli funkcja anonimowa ekstrahuje się do wyznaczonego jednego, to działa dobrze:

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

lub nawet:

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

Dlaczego istnieje różnica między działaniem nazwanych i anonimowych funkcji?

Odpowiedz

4

Makro wątku widzi i zmienia każdy podformularz w serii przed oceną tej formy, rekursywnie wstawiając poprzedni formularz jako pierwszy argument do każdego podformularza.

zacząć:

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

staje:

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

staje:

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

co oczywiście nie jest to, czego chciał w ogóle.

Ale zobaczmy, co się stanie, jeśli dodać dodatkowe parens wokół funkcji anonimowej:

(-> {: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}})) 

w ostatniej rekurencyjnego macroexpansion formularza -> teraz jesteśmy w lewo z ważnego kodu, który robi to, co chcesz.

+0

Hmm, ciekawe, dzięki. Muszę opanować makra - oczywiście "pierwszy argument do każdego podformularza" nie oznacza "pierwszego argumentu anonimowej funkcji" - tak początkowo myślałem. –

+0

tak, makra są bardzo dosłowne, więc widzi i zmienia nie 'fn', ale wywołanie' fn'. – noisesmith

3

Aby uzupełnić reakcję noisesmitha, w tym przypadku nie potrzebujesz makro wątku. Idiomatyczny sposób uzyskania wartości z zagnieżdżonej mapy to get-in. np:

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

=>

2 
+0

Jeszcze lepiej :) Dzięki. –