通过Clojure中的任意函数管道数据

时间:2010-09-10 08:38:49

标签: clojure

我知道->表单可用于将一个函数结果的结果传递给另一个:

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

(摘自excellent Clojure tutorial at ociweb

但是,此表单要求您知道要在设计时使用的功能。我想做同样的事情,但在运行时使用任意函数列表。

我已经编写了这个循环函数,但是我觉得有更好的方法:

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

最好的方法是什么?

3 个答案:

答案 0 :(得分:20)

我必须承认我对clojure很新,我可能完全忽略了这一点,但这不能用comp和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

答案 1 :(得分:6)

您可以使用普通的reduce

执行此操作
(defn pipe [x fs] (reduce (fn [acc f] (f acc)) x fs))

可以缩短为:

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

像这样使用:

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

答案 2 :(得分:4)

如果函数是一系列函数,则可以使用comp来减少它以获得组合函数。在REPL:

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

apply也适用于这种情况,因为comp采用可变数量的参数:

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