如何使let体的局部递归函数

时间:2010-11-07 19:31:20

标签: recursion clojure

我正在尝试在Clojure中创建一个函数,该函数位于(let ...)函数体的本地。我尝试了以下内容,但(defn ...)定义了全局命名空间中的内容。

(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)

如何创建一个名称空间为let主体的本地函数并且可以递归调用自身的函数?

4 个答案:

答案 0 :(得分:10)

为此,您可以使用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-construction更改为cond,我认为它更具可读性。我也分别将(+ exp 1)和( - exp 1)改为(inc exp)和(dec exp)。您甚至可以使用recur和accumulator参数来改进函数,但这可能超出了您的问题的范围。另见Brian Carper在下面的评论。

答案 1 :(得分:3)

letfn是针对您的特定情况的最佳解决方案。但是,您也可以创建一个名为“匿名”的函数,如下所示:

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

但是,此样式不允许letfn执行的相互递归函数。

答案 2 :(得分:2)

除了Michel的好答案之外:对于延迟序列使用高阶函数通常允许使用consise解决方案与显式递归相比:

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

答案 3 :(得分:1)

这是一个解决方案,它是Michiel关于使用累加器的最后陈述的一个例子。这样,您就可以使用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)