Функции более высокого порядка в Clojure
Clojure потрясающий, мы все это знаем, но дело не в этом. Мне интересно, каков идиоматический способ создания и управления функциями более высокого порядка в Haskell-подобном способе. В Clojure, я могу сделать следующее:
(defn sum [a b] (+ a b))
но (sum 1)
не возвращает функцию: это вызывает ошибку. Конечно, вы можете сделать что-то вроде этого:
(defn sum
([a] (partial + a))
([a b] (+ a b)))
в этом случае:
user=> (sum 1)
#<core$partial$fn__3678 clojure.core$partial$fn__3678@1acaf0ed>
user=> ((sum 1) 2)
3
но это не похоже на правильный путь. Есть идеи?
Я не говоря о реализации
3 ответов
кто-то уже implememented этот в группе Clojure. Вы можете указать, сколько args имеет функция, и она будет карри для вас, пока не получит столько.
Почему это не происходит по умолчанию в Clojure является то, что мы предпочитаем variadic функции автоматического каррированные функции, я полагаю.
Я немного поиграл с функциями, предложенными amalloy. Мне не нравится явная спецификация количества аргументов для карри. Поэтому я создал свой пользовательский макрос. Это старый способ определить функцию высокого порядка:
(defn-decorated old-sum
[(curry* 3)]
[a b c]
(+ a b c))
Это мой новый макрос:
(defmacro defn-ho
[fn-name & defn-stuff]
(let [number-of-args (count (first defn-stuff))]
`(defn-decorated ~fn-name [(curry* ~number-of-args)] ~@defn-stuff)))
и это новый неявный способ:
(defn-ho new-sum [a b c] (+ a b c))
как вы можете видеть, нет никаких следов (Карри) и других вещей, просто определите свою функцию currified как до.
ребята, как вы думаете? Идеи? Предложения? Тю!
Alfedo
Edit: я изменил макрос в соответствии с проблемой amalloy о docstring. Это обновленная версия:
(defmacro defhigh
"Like the original defn-decorated, but the number of argument to curry on
is implicit."
[fn-name & defn-stuff]
(let [[fst snd] (take 2 defn-stuff)
num-of-args (if (string? fst) (count snd) (count fst))]
`(defn-decorated ~fn-name [(curry* ~num-of-args)] ~@defn-stuff)))
мне не нравится оператор if внутри второй привязки. Есть идеи сделать его более лаконичным?
Это позволит вам делать то, что вы хотите:
(defn curry
([f len] (curry f len []))
([f len applied]
(fn [& more]
(let [args (concat applied (if (= 0 (count more)) [nil] more))]
(if (< (count args) len)
(curry f len args)
(apply f args))))))
вот как его использовать:
(def add (curry + 2)) ; read: curry plus to 2 positions
((add 10) 1) ; => 11
условное с [nil]
предназначен для обеспечения того, чтобы каждое приложение обеспечивало некоторый прогресс вперед в состояние Карри. За этим стоит длинное объяснение, но я нашел его полезным. Если вам не нравится этот бит, вы можете установить параметры, как:
[args (concat applied more)]
В отличие от JavaScript у нас нет способа узнать arity переданной функции, и поэтому вы должны укажите ожидаемую длину. Это имеет большой смысл в Clojure[Script], где функция может иметь несколько аритов.