2013-10-24 70 views
0

我想在clojure寫一個遞歸函數。該函數返回給定collection的最大數量。如果收集是emty,那麼它應該返回nil。 我的代碼是:卡住在Clojure遞歸

(defn gum [coll] 
(if (empty? coll) 
    0 
    (max (first coll) 
    (gum (rest coll))))) 

預期結果:

(gum [1 2 98 -3]) => 98 
(gum [1 9]) => 9 
(gum []) => nil 

但我得到:

(gum [1 2 98 -3]) => 98 
(gum [1 9]) => 9 
(gum []) => 0 (not desired result - should be `nil`) 

這是因爲我一直的(empty? coll)值作爲0。如果我保留它作爲nil然後(gum [1 2 98 -3])將無法​​正常工作。如何同時將(gum [])的值作爲nil(gum [1 2 98 -3])作爲98的任何建議?

回答

1

試試這個:

(defn gum [coll] 
(if (empty? coll) 
    nil 
    (reduce max coll))) 
+0

是的,它的工作原理,但不能使用遞歸? – user227666

+0

@ user227666如果要遞歸,則返回唯一元素時,添加條件'(= 1(count coll))'。 – SaltyEgg

+0

我無法得到你,你能詳細說明嗎? – user227666

2

我想你想是這樣的:

(defn gum [[head & tail]] 
    (if (empty? tail) 
     head 
     (max head (gum tail)))) 

我用解構的,而不是firstrest在這裏,但它是一樣的:

(defn gum [coll] 
    (let [head (first coll) 
     tail (rest coll)] 
    (if (empty? tail) 
     head 
     (max head (gum tail))))) 

但是你應該儘量避免像這樣的結構,因爲Clojure無法優化它。嘗試使用尾遞歸與recur儘可能:

(defn gum [[head & tail]] 
    (if (empty? tail) 
     head 
     (recur (cons (max head (first tail)) 
        (rest tail))))) 

recur允許Clojure的使用Tail Call Optimization你的遞歸調用轉換成一個迭代,允許它在恆定的棧空間中運行。它不僅使您的代碼更快,而且還可以防止堆棧溢出。

你也應該考慮使用高階函數,而不是遞歸(爲SaltyEgg suggested):

(defn gum [coll] 
    (if-let [s (seq coll)] 
    (reduce max s))) 

在大多數情況下,他們提供了一個簡單的解決方案。他們是非常好的優化。

+1

這不是TCO友好的。 – Chiron

0

看起來你試圖重新定義max函數?

現在,如果你正在尋找了解max函數是如何工作的,它通常遊戲一個好主意,在源(source max)看在REPL:

(defn max 
    "Returns the greatest of the nums." 
    {:added "1.0" 
    :inline-arities >1? 
    :inline (nary-inline 'max)} 
    ([x] x) 
    ([x y] (. clojure.lang.Numbers (max x y))) 
    ([x y & more] 
    (reduce1 max (max x y) more))) 

注意(apply max [])將拋出一個異常,而不是返回nilArityException Wrong number of args (0) passed to: core/max clojure.lang.AFn.throwArity (AFn.java:429)

編輯: 這就是爲什麼該方法先檢查,如果我們想申請max,然後(也許)申請,由其他答案的建議:

(defn gum [coll] 
    (if-let [s (seq coll)] 
    (reduce max s))) 
0

您不應該在裏面調用相同的函數,因爲Clojure沒有尾遞歸優化(TRO)。使用(recur arg1 arg2 etc)迭代下一步。並且不要忘記添加一個if語句來離開遞歸。