2010-09-10 15 views
16

I saben que la forma -> puede ser utilizado para pasar los resultados de un resultado de la función a otro:datos de tuberías a través de funciones arbitrarias en Clojure

(f1 (f2 (f3 x))) 
(-> x f3 f2 f1) ; equivalent to the line above 

(tomada desde el excellent Clojure tutorial at ociweb)

Sin embargo esta forma requiere que conozca las funciones que desea usar en el momento del diseño. Me gustaría hacer lo mismo, pero en tiempo de ejecución con una lista de funciones arbitrarias.

He escrito esta función de bucle que lo hace, pero tengo la sensación de que hay una mejor manera:

(defn pipe [initialData, functions] 
    (loop [ 
     frontFunc (first functions) 
     restFuncs (rest functions) 
     data initialData ] 
    (if frontFunc 
     (recur (first restFuncs) (rest restFuncs) (frontFunc data)) 
     data) 
)) 

¿Cuál es la mejor manera de hacer esto?

Respuesta

20

Debo admitir que soy realmente nuevo en clojure y me podría estar perdiendo el sentido aquí por completo, pero ¿no se puede hacer esto solo con comp y aplicar?

user> (defn fn1 [x] (+ 2 x)) 
user> (defn fn2 [x] (/ x 3)) 
user> (defn fn3 [x] (* 1.2 x)) 
user> (defn pipe [initial-data my-functions] ((apply comp my-functions) initial-data)) 
user> (pipe 2 [fn1 fn2 fn3]) 
2.8 
+0

o sin definir las funciones de antemano: (tubería 2 # (+ 2%) # (/% 3) # (* 1,2%)) – jandot

+0

Sí tienes razón, yo' También soy nuevo en Clojure y todavía no me había topado con comp. Pero, ¿y si tengo una lista de funciones, en lugar de argumentos recopilados? ¿Cómo rompo la lista para que comp la acepte? – tenpn

+0

... porque sin ese detalle, no puedo usar funciones de tiempo de ejecución arbitrarias, y en realidad es el mismo problema replanteado. – tenpn

4

Si las funciones son una secuencia de funciones, puede reducirlas usando comp para obtener una función compuesta. En una REPL:

user> (def functions (list #(* % 5) #(+ % 1) #(/ % 3))) 
#'user/my-list 
user> ((reduce comp functions) 9) 
20 

aplicará también funciona en este caso porque los lleva a un número variable de argumentos:

user> (def functions (list #(* % 5) #(+ % 1) #(/ % 3))) 
#'user/my-list 
user> ((apply comp functions) 9) 
20 
6

Usted puede hacer esto con un viejo y simple reduce:

(defn pipe [x fs] (reduce (fn [acc f] (f acc)) x fs)) 

Eso se puede acortar a:

(defn pipe [x fs] (reduce #(%2 %1) x fs)) 

usados ​​como esto:

user> (pipe [1 2 3] [#(conj % 77) rest reverse (partial map inc) vec]) 
[78 4 3] 
Cuestiones relacionadas