Подтвердить что ты не робот

Функции более высокого порядка в 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 [email protected]>
user=> ((sum 1) 2)
3

Но это не похоже на правильный путь. Любые идеи?
Я не говорю о реализации функции sum, я говорю на более высоком уровне абстракции. Существуют ли какие-либо идиоматические шаблоны? Некоторые макросы? Является ли лучший способ определения макроса или существуют альтернативные решения?

4b9b3361

Ответ 1

Кто-то уже выполнил это в группе Clojure. Вы можете указать, сколько аргументов имеет функция, и оно будет зависеть от вас до тех пор, пока оно не получит столько.

Причина, по которой это не происходит по умолчанию в Clojure, заключается в том, что мы предпочитаем вариационные функции для автоматических валют. Я полагаю.

Ответ 2

Я немного поиграл с функциями, предложенными amalloy. Мне не нравится явная спецификация числа аргументов для curry on. Поэтому я создал свой собственный макрос. Это старый способ специфической функции высокого порядка:

(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)] [email protected])))

И это новый неявный способ:

(defn-ho new-sum [a b c] (+ a b c))

Как вы можете видеть, нет следов (карри) и других вещей, просто определите свою текущую функцию как раньше.

Ребята, что вы думаете? Идеи? Предложения? Bye!

Alfedo

Изменить: я изменил макрос в соответствии с проблемой 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)] [email protected])))

Мне не нравится оператор if во второй привязке. Любые идеи о том, чтобы сделать его более сукцином?

Ответ 3

Это позволит вам делать то, что вы хотите:

(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 как:

[args (concat applied more)]

В отличие от JavaScript мы не имеем возможности узнать о arity переданной функции и поэтому вы должны указать ожидаемую длину. Это имеет большое значение в Clojure [Script], где функция может иметь несколько arities.