Warning: file_get_contents(/data/phpspider/zhask/data//catemap/3/clojure/3.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Recursion 带素因子的Clojure尾递归_Recursion_Clojure_Tail Recursion_Prime Factoring - Fatal编程技术网

Recursion 带素因子的Clojure尾递归

Recursion 带素因子的Clojure尾递归,recursion,clojure,tail-recursion,prime-factoring,Recursion,Clojure,Tail Recursion,Prime Factoring,我试着教自己clojure,我用基本因子Kata和TDD的原理来做到这一点 通过以下一系列Midje测试: (fact (primefactors 1) => (list)) (fact (primefactors 2) => (list 2)) (fact (primefactors 3) => (list 3)) (fact (primefactors 4) => (list 2 2)) 我能够创建以下函数: (defn primefactors (

我试着教自己clojure,我用基本因子Kata和TDD的原理来做到这一点

通过以下一系列Midje测试:

(fact (primefactors 1) => (list))

(fact (primefactors 2) => (list 2))

(fact (primefactors 3) => (list 3))

(fact (primefactors 4) => (list 2 2))
我能够创建以下函数:

(defn primefactors 
    ([n] (primefactors n 2))
    ([n candidate] 
        (cond (<= n 1) (list)
              (= 0 (rem n candidate)) (conj (primefactors (/ n candidate)) candidate)
              :else (primefactors n (inc candidate))
        )
    )
)
最后出现了堆栈溢出错误。我知道我需要将其转化为一个适当的递归循环,但我看到的所有示例似乎都过于简单,只指向一个计数器或数值变量作为焦点。如何使其递归


谢谢

您的第二个递归调用已经处于尾部位置,您可以将其替换为
recur

(primefactors n (inc candidate))
变成

(recur n (inc candidate))
任何函数重载都会打开一个隐式
循环
块,因此不需要手动插入该块。这应该已经在一定程度上改善了堆栈情况,因为这一分支将更常见

第一递归

(primefactors (/ n candidate))

不在尾部位置,因为其结果被传递到
conj
。要将其置于尾部位置,您需要在一个附加的累加器参数中收集基本因子,然后在每次调用时将当前递归级别的结果传递到该参数上。您需要调整终止条件以返回累加器。

典型的方法是将累加器作为函数参数之一。在函数定义中添加3-arity版本:

(defn primefactors
  ([n] (primefactors n 2 '()))
  ([n candidate acc]
    ...)
然后修改
(conj…
表单以调用
(recur…
,并将
(conj acc candidate)
作为第三个参数传递。确保将三个参数传递给
recur
,即
(recur(/n候选者)2(conj acc候选者))
,以便调用
primefactors的三元版本


(这是
primefactors
过程的尾部递归实现,它应该在不引发堆栈溢出错误的情况下工作:

(defn primefactors 
  ([n] 
    (primefactors n 2 '()))
  ([n candidate acc]
    (cond (<= n 1) (reverse acc)
          (zero? (rem n candidate)) (recur (/ n candidate) candidate (cons candidate acc))
          :else (recur n (inc candidate) acc))))
(定义参数)
([n]
(素数2’())
([n候选人acc]

(cond(尾部递归、无累加器、惰性序列解决方案:

(defn prime-factors [n]
  (letfn [(step [n div]
            (when (< 1 n)
              (let [q (quot n div)]
                (cond
                  (< q div)           (cons n nil)
                  (zero? (rem n div)) (cons div (lazy-step q div))
                  :else               (recur n (inc div))))))
          (lazy-step [n div]
            (lazy-seq
              (step n div)))]
    (lazy-step n 2)))
(定义基本因子[n]
(letfn[(步骤[n部分]
(当(<1 n)
(让[q(quot n div)]
(续)
(

嵌入在
lazy seq
中的递归调用在对序列进行迭代之前不会进行评估,从而消除了堆栈溢出的风险,而无需使用累加器。

此函数实际上不应该是尾部递归的:它应该构建一个延迟序列。毕竟,知道
461168601842738790不是很好吗2是非素数(它可以被2整除),而不必计算数字并发现它的另一个素数因子是
2305843009213693951

(defn prime-factors
  ([n] (prime-factors n 2))
  ([n candidate]
     (cond (<= n 1) ()
           (zero? (rem n candidate)) (cons candidate (lazy-seq (prime-factors (/ n candidate)
                                                                              candidate)))
           :else (recur n (inc candidate)))))
(定义基本因子)
([n](素因子n2))
([n候选人]

(cond)(谢谢,这太棒了,这是我需要的解释。在Clojure中,“递归然后反转”是一种反模式:我们有向量,它们在右边附加得很便宜,所以最好从正确的顺序开始构建列表(如果你需要一个列表,而不是最后的向量,只需
seq
it,这比反向更便宜)。但实际上,惰性解决方案比尾部递归解决方案更可取:请看我的答案,看一个简单的例子。哇。这是我第一次看到编写Lisp的人真正给出自己的行:P
(defn prime-factors
  ([n] (prime-factors n 2))
  ([n candidate]
     (cond (<= n 1) ()
           (zero? (rem n candidate)) (cons candidate (lazy-seq (prime-factors (/ n candidate)
                                                                              candidate)))
           :else (recur n (inc candidate)))))