2011-03-16 165 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 [腳本]中有很多意義,其中函數可能有多個屬性。