zoukankan      html  css  js  c++  java
  • 尾调用优化和尾递归改写

    1 尾调用

    尾调用就是指某个函数的最后一步是调用另一个函数。

    # 是尾调用
    def f(x):
        return g(x)
    
    # 不是尾调用,因为调用函数后还要执行加法,加法才是最后一步操作
    def f(x):
        return 1+g(x)
    

    2 尾调用优化

    函数调用有一个调用栈,栈内保存了这个函数内部的变量信息。函数掉用就是切换不同的调用帧,从而保证每个函数有独立的运行环境。因为尾调用是函数的最后一步操作,所以在进入被尾调用函数之前并不需要保留外层函数的运行时环境,因为调用位置、内部变量等信息都不会再用到了,只要直接用内层函数的调用记录,取代外层函数的调用记录就可以了。这就叫做"尾调用优化"(Tail call optimization),即只保留内层函数的调用记录。如果所有函数都是尾调用,那么完全可以做到每次执行时,调用记录只有一项,这将大大节省内存。这就是"尾调用优化"的意义。

    尾递归

    如果尾调用自身,就称为尾递归。递归非常耗费内存,因为需要同时保存成千上百个调用记录,很容易发生"栈溢出"错误(stack overflow)。但对于尾递归来说,由于只存在一个调用记录,所以永远不会发生"栈溢出"错误。

    def factorial(n) {
      if (n == 1) return 1;
      return n * factorial(n - 1);
    }
    
    factorial(5) // 120
    

    上面代码是一个阶乘函数,计算n的阶乘,最多需要保存n个调用记录,复杂度 O(n) 。如果改写成尾递归,只保留一个调用记录,复杂度 O(1) 。

    def factorial(n, total) {
      if (n == 1) return total;
      return factorial(n - 1, n * total);
    }
    
    factorial(5, 1) // 120
    

    4 递归函数的改写

    尾递归的实现,往往需要改写递归函数,确保最后一步只调用自身。做到这一点的方法,就是把所有用到的内部变量改写成函数的参数。比如上面的例子,阶乘函数 factorial 需要用到一个中间变量 total ,那就把这个中间变量改写成函数的参数。这样做的缺点就是不太直观,第一眼很难看出来,为什么计算5的阶乘,需要传入两个参数5和1?

    两个方法可以解决这个问题。方法一是在尾递归函数之外,再提供一个正常形式的函数。

    def tailFactorial(n, total) {
      if (n === 1) return total;
      return tailFactorial(n - 1, n * total);
    }
    
    def factorial(n) {
      return tailFactorial(n, 1);
    }
    
    factorial(5) // 120
    

    上面代码通过一个正常形式的阶乘函数 factorial ,调用尾递归函数 tailFactorial ,看起来就正常多了。

  • 相关阅读:
    【leetcode】1020. Partition Array Into Three Parts With Equal Sum
    【leetcode】572. Subtree of Another Tree
    【leetcode】123. Best Time to Buy and Sell Stock III
    【leetcode】309. Best Time to Buy and Sell Stock with Cooldown
    【leetcode】714. Best Time to Buy and Sell Stock with Transaction Fee
    【leetcode】467. Unique Substrings in Wraparound String
    【leetcode】823. Binary Trees With Factors
    【leetcode】143. Reorder List
    【leetcode】1014. Capacity To Ship Packages Within D Days
    【leetcode】1013. Pairs of Songs With Total Durations Divisible by 60
  • 原文地址:https://www.cnblogs.com/ZeroTensor/p/10503607.html
Copyright © 2011-2022 走看看