Higher-order functions in Clojure
Clojure很棒,我们都知道,但这不是重点。 我想知道以类似Haskell的方式创建和管理高阶函数的惯用方式是什么。 在Clojure中,我可以执行以下操作:
1 | (defn sum [a b] (+ a b)) |
但是
1 2 3 | (defn sum ([a] (partial + a)) ([a b] (+ a b))) |
在这种情况下:
1 2 3 4 | user=> (sum 1) #<core$partial$fn__3678 clojure.core$partial$fn__3678@1acaf0ed> user=> ((sum 1) 2) 3 |
但这似乎不是正确的方法。 有任何想法吗?
我不是在谈论实现
已经有人在Clojure小组中实现了这一点。您可以指定一个函数有多少个args,它将自动为您处理直到获得那么多。
我想,在Clojure中默认情况下不会发生这种情况的原因是,我们认为可变参数函数比自动咖喱函数更喜欢。
我已经使用了alalloy建议的功能。我不喜欢明确说明要讨论的参数数量。因此,我创建了自定义宏。这是特定于高阶函数的旧方法:
1 2 3 4 | (defn-decorated old-sum [(curry* 3)] [a b c] (+ a b c)) |
这是我的新宏:
1 2 3 4 | (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))) |
这是新的隐式方式:
1 | (defn-ho new-sum [a b c] (+ a b c)) |
如您所见,没有(curry)和其他东西的痕迹,只需像以前一样定义您的currified函数即可。
伙计们,您怎么看?有想法吗?有什么建议吗?
再见!
阿尔费多
编辑:我已经根据有关docstring的amalloy问题修改了宏。这是更新的版本:
1 2 3 4 5 6 7 | (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语句。关于使其更简洁有什么想法?
这将使您可以执行所需的操作:
1 2 3 4 5 6 7 8 | (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)))))) |
使用方法如下:
1 2 | (def add (curry + 2)) ; read: curry plus to 2 positions ((add 10) 1) ; => 11 |
1 | [args (concat applied more)] |
与JavaScript不同,我们无法知道所传递函数的有效性,因此必须指定所需的长度。这在Clojure [Script]中很有意义,其中一个函数可能具有多个arities。