36

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功能,而是在更高的抽象层次上谈论。是否有任何惯用的模式可以遵循?一些宏?是定义宏的最佳方法还是有替代解决方案?

4

3 回答 3

32

有人已经在 Clojure 组上实现了这一点。你可以指定一个函数有多少个参数,它会为你自己咖喱,直到它得到那么多。

在 Clojure 中默认情况下不会发生这种情况的原因是我们更喜欢可变参数函数而不是自动柯里化函数,我想。

于 2011-03-16T17:22:29.207 回答
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))

如您所见,没有 (curry) 和其他东西的痕迹,只需像以前一样定义您的 currified 函数。

小伙伴们,你们怎么看?想法?建议?再见!

阿尔费多

编辑:我已经根据有关文档字符串的 amalloy 问题修改了宏。这是更新版本:

(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 语句。关于使它更简洁的任何想法?

于 2011-03-16T18:17:05.037 回答
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]旨在确保每个应用程序都确保向 curried 状态前进。它背后有一个很长的解释,但我发现它很有用。如果您不喜欢这一点,可以将 args 设置为:

[args (concat applied more)]

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

于 2016-12-16T21:23:57.883 回答