我正在尝试在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主体的本地函数并且可以递归调用自身的函数?
答案 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)