Данные о трубопроводе через произвольные функции в Clojure
Я знаю, что форма ->
может использоваться для передачи результатов одного результата функции другому:
(f1 (f2 (f3 x)))
(-> x f3 f2 f1) ; equivalent to the line above
(взято из отличный Clojure учебник в ociweb)
Однако эта форма требует, чтобы вы знали функции, которые вы хотите использовать во время разработки. Я хотел бы сделать то же самое, но во время выполнения со списком произвольных функций.
Я написал эту функцию цикла, которая делает это, но у меня есть ощущение, что есть лучший способ:
(defn pipe [initialData, functions]
(loop [
frontFunc (first functions)
restFuncs (rest functions)
data initialData ]
(if frontFunc
(recur (first restFuncs) (rest restFuncs) (frontFunc data) )
data )
) )
Какой лучший способ сделать это?
Ответы
Ответ 1
Должен признаться, что я действительно новичок в clojure, и я могу вообще пропустить этот пункт целиком, но не могу ли это просто сделать, используя comp и применить?
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
Ответ 2
Вы можете сделать это с помощью простого старого 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]
Ответ 3
Если функция представляет собой последовательность функций, вы можете уменьшить ее с помощью 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