Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Piping data through arbitrary functions in Clojure

Tags:

clojure

I know that the -> form can be used to pass the results of one function result to another:

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

(taken from the excellent Clojure tutorial at ociweb)

However this form requires that you know the functions you want to use at design time. I'd like to do the same thing, but at run time with a list of arbitrary functions.

I've written this looping function that does it, but I have a feeling there's a better way:

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

What's the best way to go about this?

like image 357
tenpn Avatar asked Sep 10 '10 08:09

tenpn


2 Answers

I must admit I'm really new to clojure and I might be missing the point here completely, but can't this just be done using comp and apply?

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
like image 154
jandot Avatar answered Oct 23 '22 02:10

jandot


You can do this with a plain old reduce:

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

That can be shortened to:

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

Used like this:

user> (pipe [1 2 3] [#(conj % 77) rest reverse (partial map inc) vec])
[78 4 3]
like image 6
Brian Carper Avatar answered Oct 23 '22 04:10

Brian Carper