2010-04-05 6 views
5

Se faccio la seguente:(parziale applicare str) e applicare-str in di clojure ->

user=> (-> ["1" "2"] (partial apply str)) 
#<core$partial__5034$fn__5040 [email protected]> 

... ottengo una funzione parziale schiena. Tuttavia, se lo associo a una variabile:

user=> (def apply-str (partial apply str)) 
#'user/apply-str 
user=> (-> ["1" "2" "3"] apply-str)  
"123" 

... il codice funziona come previsto. Suppongo che siano la stessa cosa, ma a quanto pare non è così. Qualcuno può spiegare perché questo è per me?

risposta

6

-> è una macro, quindi non deve seguire le regole che ci si aspetterebbe in termini di applicazione. La macro trasforma la sorgente prima che i moduli vengano valutati. Prova la macroespansione dei moduli:

user> (macroexpand '(-> ["1" "2"] (partial apply str))) 
(partial ["1" "2"] apply str) 

Che cosa stai cercando di ottenere qui utilizzando la macro '->'?

EDIT: Si noti che:

user> ((partial apply str) ["1" "2"]) 
"12" 
+0

Il codice che sto cercando di scrivere è un po 'più complesso di quello. Questo è solo un esempio semplificato. Voglio solo capire meglio come funziona la macro '->'. :-) –

+0

Ah, gotcha. Ad ogni modo, vedi cosa sta succedendo qui? –

+0

Sì, sì. Grazie per la tua risposta! –

5

Non devi farlo a tutti.

(->> ["1" "2" "3"] (apply str)) 

Perché non farlo invece?

4

La prima espressione, , si espande in:

(partial ["1" "2"] apply str) che significa sostanzialmente:

creare una funzione da ["1" "2"] (che è anche una funzione, in quanto i vettori sono funzioni di chiavi indice!) Con i Vars apply e str già forniti come primi due argomenti. Questa funzione viene stampata come la strana stringa #<core$partial...>. Solo quando questa funzione verrà chiamata otterrete un IllegalArgumentException poiché i vettori prendono solo un argomento intero, non due argomenti Var.

+0

si prega di notare che Clojure essendo dinamico il fatto che i vettori siano funzioni non ha importanza per questo bug. (parziale "ciao" "mondo") restituisce anche una funzione, una funzione che però sempre lancia. – cgrand

0

La macro -> aggiunge i parenti intorno a apply-str nella seconda versione, ecco perché la macro si espande al codice che finisce per chiamare la funzione. Guardare il codice sorgente per -> e si può vedere:

(defmacro -> 
    "Threads the expr through the forms. Inserts x as the 
    second item in the first form, making a list of it if it is not a 
    list already. If there are more forms, inserts the first form as the 
    second item in second form, etc." 
    ([x] x) 
    ([x form] (if (seq? form) 
       (with-meta `(~(first form) ~x [email protected](next form)) (meta form)) 
       (list form x))) 
    ([x form & more] `(-> (-> ~x ~form) [email protected]))) 

La parte rilevante è quando si sta trattando con due argomenti, x e form. Se form è un seq, x viene inserito come secondo argomento in tale elenco. In caso contrario, la macro inserisce form e x in una lista stessa. In questo modo è possibile utilizzare un simbolo nudo come abbreviazione di un elenco contenente un simbolo.

user> (macroexpand '(-> 123 (foo))) 
(foo 123) 
user> (macroexpand '(-> 123 foo)) 
(foo 123) 
1

Il Macro -> discussioni expr attraverso le forme come secondo argomento. Nel tuo caso finisce espandendo a: (partial ["1" "2"] apply str), creando una funzione parentale basata sul vettore.

ma si vuole richiamare una funzione parital sulla base di applicare e str sulla espr filettato e quindi hanno bisogno di:

(-> ["1" "2"] ((partial apply str))) 

Bene: questo codice i molto confusa e non idiomatica Clojure.