2012-08-01 18 views
7

Dato un oleodotto ->> in questo modo:elementi condizionali in ->/- >> oleodotti

(defn my-fn [] 
    (->> (get-data) 
     (do-foo) 
     (do-bar) 
     (do-baz))) 

desidero fare le varie fasi condizionale.

Il primo modo di scrivere questo che è venuto in mente era come tale:

(defn my-fn [{:keys [foo bar baz]}] 
    (->> (get-data) 
     (if foo (do-foo) identity) 
     (if bar (do-bar) identity) 
     (if baz (do-baz) identity)) 

Tuttavia, poiché i ->> macro tenta di inserire nel modulo if, questo sembra un peccato non solo in termini di prestazioni (avendo noop identity chiamate), ma in realtà non riesce a compilare.

Quale sarebbe un modo appropriato, ragionevolmente ASCIUTTO di scrivere questo?

+0

'get-data' restituisce una sequenza come di solito viene utilizzata con' - >> '? La funzione 'do-' chiama gli effetti collaterali o prende/restituisce qualcosa? –

+0

Sembra improbabile che il se nella chiamata al thread faccia del male alle prestazioni in modo apprezzabile. –

+0

@AlexTaggart Sì, 'get-data' restituisce una sequenza, potenzialmente piuttosto lunga, rendendola effettivamente un ciclo interno. Le funzioni 'do- *' sono pure, senza effetti collaterali; forse ho chiamato male il codice nell'esempio. –

risposta

6

Clojure moderna (cioè, a partire dal 1.5) supporta una varietà di opzioni per il threading condizionale.

as-> è forse il più versatile, dandoti un nome per fare riferimento alla "cosa" che viene infilata nelle forme. Per esempio:

(as-> 0 n 
    (inc n) 
    (if false 
    (inc n) 
    n)) 
=> 1 

(as-> 0 n 
    (inc n) 
    (if true 
    (inc n) 
    n)) 
=> 2 

Questo dà una notevole flessibilità quando si lavora con un mix di funzioni che richiedono filettatura l'espressione in diversi punti della lista di parametri (cioè, il passaggio da -> a - >> sintassi). Si dovrebbe evitare l'uso di variabili con nome estranee nell'interesse della leggibilità del codice, ma spesso questo è il modo più chiaro e semplice per esprimere un processo.

C'è anche cond->, che richiede un set di coppie: un test e un'espressione se quel test viene valutato come true. Questo è abbastanza simile a cond ma non si ferma al primo vero test.

4

è possibile fissare la parte compilazione (anche se non la parte ascetica avendo il if decidere quale funzione da eseguire mettendo un'altra serie di () come così:

(defn my-fn [{:keys [foo bar baz]}] 
    (->> (get-data) 
    ((if foo do-foo identity)) 
    ((if bar do-bar identity)) 
    ((if baz do-baz identity))) 

si espanderebbe in una serie di chiama in questo modo:

; choose function   and call it  
    ((if foo do-foo identity) args  ) 
    ((if bar do-bar identity) args  ) 
    ((if baz do-baz identity) args  ) 
+0

Grazie!Ho citato i miei fns, cioè '((se foo 'do-foo' identità) args)' - il tuo esempio mi ha aiutato a eseguire il debug del mio problema! –

6

questo funziona troppo:

(defn my-fn [{:keys [foo bar baz]}] 
    (->> (get-data) 
     (#(if foo (do-foo %) %)) 
     (#(if bar (do-bar %) %)) 
     (#(if baz (do-baz %) %)))) 
4

Un modo più generale per andare se avete bisogno di questo genere di cose spesso:

 
(defn comp-opt [& flag-fns] 
    (->> flag-fns 
    (partition 2) 
    (filter first) 
    (map second) 
    (apply comp))) 

(defn my-fn [{:keys [foo bar baz]}] 
    (map (comp-opt foo do-foo 
       bar do-bar 
       baz do-baz) 
     (get-data))) 
7

Potreste essere interessati a queste macro https://github.com/pallet/thread-expr

+0

+1 per suggerire di non reinventare la ruota. :) Ho usato la tua libreria expr thread ed è semplice, piccola, elegante e funziona bene. – Gert

+0

Stai proponendo '(when-> foo do-foo)', o un diverso utilizzo? –

Problemi correlati