Clojure中的高阶函数

时间:2011-03-16 17:17:05

标签: clojure currying higher-order-functions

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

但这似乎不是正确的方法。任何想法?
我不是在谈论实现sum函数,而是在更高层次的抽象中进行讨论。是否有任何惯用模式可供遵循?有些宏?是定义宏的最佳方式还是有替代解决方案?

3 个答案:

答案 0 :(得分:32)

有人在Clojure小组上already implememented this。你可以指定一个函数有多少个args,它会为你自己调整,直到得到那么多。

我认为,在Clojure中默认情况下不会发生这种情况的原因是我们更喜欢使用可变参数函数来自动计算函数。

答案 1 :(得分:8)

我玩过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

编辑:我根据有关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语句。关于让它变得更加舒适的任何想法?

答案 2 :(得分:0)

这将允许您按照自己的意愿行事:

(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]的条件是为了确保每个应用程序确保向curry状态前进一步。背后有一个很长的解释,但我发现它很有用。如果您不喜欢这一点,可以将args设置为:

[args (concat applied more)]

与JavaScript不同,我们无法知道传递函数的arity,因此您必须指定所期望的长度。这在Clojure [Script]中很有意义,其中一个函数可能有多个arities。