2010-11-07 47 views
4

我試圖在Clojure中創建一個函數(let ...)函數的本體函數。我嘗試了以下,但(定義...)在全局名稱空間中定義的東西。現在如何對let body本地創建遞歸函數

(let [] (defn power [base exp] 
     (if (= exp 0) 
     1 
     (if (> exp 0) 
      ; Exponent greater than 0           
      (* base (power base (- exp 1))) 
      ; Exponent less than 0            
     (/ (power base (+ exp 1)) base)))) 
(println (power -2 3))) 

; Function call outside of let body 
(println (power -2 3)) 

,我也試過:

(let [power (fn [base exp] 
     (if (= exp 0) 
     1 
     (if (> exp 0) 
      ; Exponent greater than 0           
     (* base (power base (- exp 1))) 
      ; Exponent less than 0            
     (/ (power base (+ exp 1)) base))))] 
(println (power -2 3))) 

; Function call outside of let body 
(println (power -2 3)) 

但後來我得到的錯誤:

Exception in thread "main" java.lang.Exception: Unable to resolve symbol: power in this   context (math.clj:6) 

我如何,其名稱空間是本地令身體能功能遞歸調用自己?

回答

9

爲此,您可以使用letfn

(letfn [(power [base exp] 
       (cond 
       (= exp 0) 
       1 
       (> exp 0) ; Exponent greater than 0        
       (* base (power base (dec exp))) 
       :else ; Exponent less than 0          
       (/ (power base (inc exp)) base)))] 
     (print (power -2 3))) 

請注意,我也改變了你的嵌套的if-建設COND,我認爲這是更具可讀性。此外,我分別將(+ exp 1)和( - exp 1)更改爲(inc exp)和(dec exp)。你甚至可以像使用遞歸和累加器參數一樣改進你的函數,但也許這超出了你的問題的範圍。另請參閱下面的Brian Carper的評論。

+0

'(= exp 0)'=>'(zero?exp)'; '(> exp 0)'=>'(pos?exp)'; '(< exp 0)' =>'(neg?exp)' – 2010-11-10 00:38:21

2

Besides至Michel的good答案:Using high order function上lazy sequences往往allow consise solutions compared到explicit recursion:

(defn power [base exp] 
    (reduce * (repeat exp base))) 
+0

這個版本不允許否定exp。 – 2010-11-08 00:02:27

1

Here是一個solution which是about using an accumulator Michiel's last statement的an example 。這使您可以使用recur來優化tail call優化。這爲您提供了每次遞歸不消耗堆棧空間的優勢。

(defn pow [base exp] 
(letfn [(power [base exp accum] 
       (cond 
        (= exp 0) accum 
        (> exp 0) (recur base (dec exp) (* accum base)) 
        :else (recur base (inc exp) (/ accum base))))] 
(power base exp 1))) 

user> (pow -3 2) 
9 
user> (pow -3 3) 
-27 

如果你只是希望寫一個函數,它確實提出了一個基數的功率不要忘記你能叫出到已經存在於Java中的方法。 java.util.Math可以在這裏幫助你。

(Math/pow -2 3) 
3

letfn是您的特殊情況的最佳解決方案。但是,你也可以創建像這樣一個名爲「匿名」的功能:

(let [power (fn power [base exp] ...)] 
    (println (power -2 3))) 

然而,這種風格不允許相互遞歸功能,letfn一樣。

+1

不是。這不是'letfn'擴展到''letfn'允許相互遞歸的函數,這個樣式沒有。 – kotarak 2010-11-08 07:39:57

+0

@ kotarak - 好的,我不知道,我會更新我的答案! – harto 2010-11-09 00:07:43