简体   繁体   English

尾递归调用尾递归

[英]Tail recursion calling tail recursion

I'm trying to solve the pascal's triangle with tail recursion. 我正在尝试通过尾部递归来解决帕斯卡的三角形。 I understand to do tail recursion, the function call statement should be the last instruction. 我知道要执行尾递归,函数调用语句应该是最后一条指令。 Like here: 像这儿:

(defn pascal [line colum, acc]
  (if (or (= line 0) (= line colum) (= colum 0))
    (+ acc 1)
    (recur (dec line) colum
           (pascal (dec line) (dec colum), acc))))

My question is: Since I use a recursive call as a parameter for my recursion, is it still valid? 我的问题是:由于我使用递归调用作为递归的参数,它仍然有效吗?

Because I can not replace this: 因为我不能代替这个:

(recur (dec line) colum
       (pascal (dec line) (dec colum), acc))))

To this: 对此:

(recur (dec line) colum
       (recur (dec line) (dec colum), acc))))

Best Regards 最好的祝福

Only half of your calls are by tail recursion so the other half can blow the stack. 您的调用中只有一半是通过尾递归进行的,因此另一半会破坏堆栈。 Compare it with this: 与此比较:

(defn factorial (n) 
  (if (= n 1)
      1
      (* n (factorial (n - 1)))))

Here (factorial (n - 1)) needs to finish before the continuation (* n <result>) which is a stack frame waiting while the recursion is running. 这里(factorial (n - 1))需要在延续(* n <result>)之前完成,延续是递归运行时等待的堆栈帧。

It's better than neither being tail calls, but its much better if all are and it is possible! 这总比没有成为尾声更好,但是如果一切皆有可能那就更好了

Maybe my answer is unrelated to an recursion, and this question at all unlike the answer by @Sylwester , but it's still useful to show an another way to solve this problem. 也许我的答案与递归无关,而且这个问题与@Sylwester的答案完全不同,但是显示另一种解决此问题的方法仍然很有用。

Assuming that pascal triangle has this properties: 假设Pascal三角形具有以下属性:

  1. First and last item in every line of pascal triangle is '1' 帕斯卡三角形的每一行的第一个和最后一个项目为“ 1”
  2. Second and penultimate is number of line 第二倒数是行数
  3. Any other elements can be solved by formula: 任何其他元素均可通过以下公式求解:

Than means, that You can solve any elements of pascal triangle by linear algorithm with time complexity O(n^3). 这意味着,您可以通过线性算法求解时间轴为O(n ^ 3)的帕斯卡三角形的任何元素。 It may be not much cool than recursive version with O(n^2) and recursion, but it doesn't blow stack and it use combinatorics, which is in my opinion even better, because it's simpler and safer version. 它可能比O(n ^ 2)和递归的递归版本还差劲,但是它不会破坏堆栈,它使用了组合函数,我认为更好,因为它更简单,更安全。 So, here we go: 所以,我们开始:

(defn naive-factorial[n]
  (reduce *' (range 1 (inc n))))

(defn combinatoric-formula[line pos]
  (if (<= pos line)
    (/ 
     (naive-factorial line) 
     (*' (naive-factorial pos) 
         (naive-factorial (- line pos))))))

As You can see there is used naive-factorial function, which takes n multiplication, which lead us to O(n^3). 如您所见,使用了naive-factorial函数,该函数乘以n ,从而得出O(n ^ 3)。 It's the same as your function, but without any recursion. 它与您的函数相同,但是没有任何递归。

For pascal triangle there is also much than one way to solve them in different ways. 对于帕斯卡三角形,还有许多方法可以用不同的方式解决它们。 Some of them are very tricky, take a look, if you have much time: rosettacode.org 如果您有很多时间,其中一些技巧非常棘手: rosettacode.org

Also in you version your use int math in clojure by + , please use in +' function in any cases which can lead to large numbers(assuming that that means that the addition will be lead to converting your values to biginteger type, which allows very large numbers). 同样在您的版本中,您可以通过+在clojure中使用int数学,请在任何情况下都可以在+'函数中使用in +' ,这会导致产生大量数字(假设这意味着加法会将您的值转换为biginteger类型,这将非常大数)。

Also I translated the scheme version introduced by @Sylwester to a clo j ure: 此外,我翻译的@Sylwester介绍给CLOĴ茜方案版本:

(defn pascal [row col]
  (let [aux  
        (fn aux [tr tc prev acc]
          (cond (> tr row) (throw (.Exception "invalid input"))         
                (and (= col tc) (= row tr)) (+' (first prev) (second prev)); the next number is the answer
                (= tc tr) (recur (+' tr 1) 1 (cons 1 acc) '(1))                  ; new row 
                :else (recur tr               ; new column
                           (+' tc 1) 
                           (next prev)
                           (cons (+' (first prev) (second prev)) acc))))]
    (if (or (zero? col) (= col row))
      1
      (aux 2 1 '(1 1) '(1)))))

It's maybe can be improved, but shows the idea. 也许可以改进,但可以说明问题。 It's calculated the entire triangle from third line to previous one of provided input and then gets the answer. 它计算了从第三行到提供的输入的前一个的整个三角形,然后得到了答案。 Pretty awesome and pure magic of function approach. 功能方法非常棒且纯粹。

The performance of this version is much more worse, than the linear version. 这个版本的性能比线性版本差很多。 So it gets: 因此得到:

(time (combinatoric-formula 1000 100))
"Elapsed time: 2.73794 msecs" for linear version

(time (pascal 1000 100))
"Elapsed time: 135.426888 msecs" for tail recursion version

But it's still much cleaner and cooler ;) 但是它仍然更加干净和凉爽;)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM