2011-03-16 126 views
36

Clojure真棒,我们都知道这一点,但那不是重点。我想知道以类似Haskell的方式创建和管理高阶函数的惯用方法是什么。在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 [email protected]> 
user=> ((sum 1) 2) 
3 

但它似乎并不像正确的前进道路。有任何想法吗?
我不是在谈论实现sum函数,我正在谈论更高层次的抽象。是否有任何习惯模式?一些宏?定义宏的最佳方式还是有其他解决方案吗?

回答

32

有人在Clojure小组上有already implememented this。你可以指定一个函数有多少个参数,并且它会为你自己粘贴,直到它达到很多。

在Clojure中,默认情况下不会发生这种情况的原因是我们更喜欢variadic函数来自动执行curried函数。

+1

感谢您的快速回复。我已经看过那篇文章,而且我想知道是否在某些clojure.contrib子包中采用了建议的解决方案,或者只是可能想法的早午餐:) – 2011-03-16 17:31:01

+0

我可以从您的答案中推断出您认为可变函数和自动粘贴功能不兼容?如果是这样,我想更多地了解其原因。谢谢。 – day 2012-09-11 14:29:41

+0

给定'(defn f([x] 1)([x y] 2))','(f true)'产生了什么?它必须是1,这意味着它不能作为二元版本的部分应用程序自动粘贴。 – amalloy 2012-09-11 17:48:53

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

这是新含蓄的方式:

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

,你可以看到没有(咖喱)和其他东西的痕迹,只是像以前一样定义你的currified功能。

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

Alfedo

编辑:我已经修改了关于根据文档字符串的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)] [email protected]))) 

我不喜欢第二个绑定中的if语句。任何想法让它更具魅力?

+3

我喜欢。虽然我会为你的宏建议一个不同的名字。也许是“defcurry”或者“defcurried”。 – 2011-03-16 18:45:54

+0

我也接受这个主题的建议.... defn-ho是可怕的,我知道:D – 2011-03-16 18:47:37

+0

'(defn-ho myfn“做了很棒的东西”[a b c] ...)'。现在你的参数计算不会很好。这当然是可以做到的,但处理定义接受的形式并不是微不足道的,我怀疑它是没有以这种方式开始实施的原因。 – amalloy 2011-03-16 20:29:37

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]的条件是为了确保每一个应用程序可确保一些前瞻性的进步到咖喱状态。背后有一个很长的解释,但我发现它很有用。如果你不喜欢这一点,你可以设置为ARGS:

[args (concat applied more)] 

不像JavaScript中,我们没有办法得知传递函数的元数的方式,所以你必须指定你所期望的长度。这在Clojure [脚本]中有很多意义,其中函数可能有多个属性。