2012-08-01 18 views
7

Dado un oleoducto ->> así:elementos condicionales en -/-> >> tuberías

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

Deseo hacer que las diferentes etapas condicional.

La primera forma de escribir esto que vino a la mente fue como tal:

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

Sin embargo, como los ->> macro intenta insertar en la forma if, esto no sólo se ve desafortunado en términos de rendimiento (que tiene las llamadas a noop identity), pero en realidad no se puede compilar.

¿Cuál sería una forma apropiada, razonablemente SECA de escribir esto?

+0

¿'get-data' devuelve una secuencia como normalmente se usa con' - >> '? ¿La función 'do-' llama a los efectos secundarios, o toma/devuelve algo? –

+0

Parece poco probable que el if en la llamada con hilos afecte negativamente al rendimiento de forma apreciable –

+0

@AlexTaggart Sí, 'get-data' devuelve una secuencia, potencialmente bastante larga, haciendo de esto un bucle interno. Las funciones 'do- *' son puras, sin efectos secundarios; tal vez nombré mal el código en el ejemplo. –

Respuesta

6

Clojure moderna (es decir, a partir de 1.5) compatible con una variedad de opciones para enhebrar condicional.

as-> es quizás el más versátil, al darle un nombre para referirse a la "cosa" que se enhebra a través de los formularios. Por ejemplo:

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

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

Esto da flexibilidad sustancial cuando se trabaja con una mezcla de funciones que requieren enhebrar la expresión en diferentes puntos en la lista de parámetros (es decir, el cambio de -> a - >> sintaxis). Se debe evitar el uso de variables con nombres extraños en interés de la legibilidad del código, pero a menudo esta es la forma más clara y sencilla de expresar un proceso.

También hay cond->, que solicita un conjunto de pares: una prueba y una expresión si esa prueba se evalúa como verdadera. Esto es bastante similar a cond pero no se detiene en la primera prueba verdadera.

4

se puede fijar la parte compilación (aunque no la parte ascética haciendo que el sentencia if decidir qué función debe ejecutar poniendo otro conjunto de () como tan:

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

se expandiría en una serie de llama así:

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

¡Gracias!He estado citando mi fns, es decir, '((si es foo 'do-foo' identity) args)' - ¡Tu ejemplo me ayudó a depurar mi problema! –

6

esto funciona también:

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

Una forma más general para ir si necesita este tipo de cosas a menudo:

 
(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

que podría estar interesado en estas macros https://github.com/pallet/thread-expr

+0

+1 por sugerir no reinventar la rueda. :) He usado tu biblioteca de thread-expr y es simple, pequeña, elegante y funciona bien. – Gert

+0

¿Está proponiendo '(when-> foo do-foo)', o un uso diferente? –