Функции высшего порядка в Clojure - PullRequest
36 голосов
/ 16 марта 2011

Clojure потрясающий, мы все это знаем, но не в этом суть.Мне интересно, каков идиоматический способ создания и управления функциями высшего порядка в стиле Хаскелла.В 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

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

Ответы [ 3 ]

32 голосов
/ 16 марта 2011

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

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

8 голосов
/ 16 марта 2011

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

(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))

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

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

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 внутри второй привязки. Есть идеи, как сделать его более лаконичным?

0 голосов
/ 17 декабря 2016

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

(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, у нас нет способа узнать арность переданной функции, поэтому вы должны указать ожидаемую длину.Это имеет большой смысл в Clojure [Script], где функция может иметь несколько арностей.

...