Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/python/335.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
Python 如何将递归转换为尾部递归_Python_Recursion_Tail Recursion - Fatal编程技术网

Python 如何将递归转换为尾部递归

Python 如何将递归转换为尾部递归,python,recursion,tail-recursion,Python,Recursion,Tail Recursion,是否总是可以将递归转换为尾部递归 我很难将下面的Python函数转换为尾部递归函数 def BreakWords(glob): """Break a string of characters, glob, into a list of words. Args: glob: A string of characters to be broken into words if possible. Returns: List of words if glob can be

是否总是可以将递归转换为尾部递归

我很难将下面的Python函数转换为尾部递归函数

def BreakWords(glob):
  """Break a string of characters, glob, into a list of words.

  Args:
    glob: A string of characters to be broken into words if possible.

  Returns:
    List of words if glob can be broken down. List can be empty if glob is ''.
    None if no such break is possible.
  """
  # Base case.
  if len(glob) == 0:
    return []

  # Find a partition.
  for i in xrange(1, len(glob) + 1):
    left = glob[:i]
    if IsWord(left):
      right = glob[i:]
      remaining_words = BreakWords(right)
      if remaining_words is not None:
        return [left] + remaining_words

  return None

我不确定是否总是这样,但大多数递归函数都可以作为尾部递归实现。此外,尾部递归不同于尾部递归优化

尾部递归和“常规”递归的区别 递归函数中必须存在两个元素:
  • 递归调用
  • 保存返回值计数的位置
  • “常规”递归函数将(2)保持在堆栈框架中。 常规递归函数中的返回值由两种类型的值组成:

    • 其他返回值
    • 函数计算结果
    让我们看一个例子:

    def factorial(n):
        if n == 1 return 1
        return n * factorial(n-1)
    
    例如,帧f(5)“存储”它自己的计算结果(5)和f(4)的值。如果我调用factorial(5),就在堆栈调用开始崩溃之前,我有:

     [Stack_f(5): return 5 * [Stack_f(4): 4 * [Stack_f(3): 3 * ... [1[1]]
    
    请注意,除了我提到的值之外,每个堆栈还存储函数的整个范围。因此,递归函数f的内存使用是O(x),其中x是我必须进行的递归调用的数量。所以,如果我需要1kb的RAM来计算阶乘(1)或阶乘(2),我需要~100k来计算阶乘(100),依此类推

    尾部递归函数将(2)放入其参数中。 在尾部递归中,我使用参数将每个递归帧中的部分计算结果传递给下一个帧。让我们看看我们的阶乘示例,Tail Recursive:

    def factorial(n):
        def tail_helper(n, acc):
            if n == 1 or n == 2: return acc
            return tail_helper(n-1, acc + n)
    return tail_helper(n,0)
    
    让我们看看阶乘(4)中的框架:

    看到区别了吗在“常规”递归调用中,返回函数递归地组成最终值。在尾部递归中,它们只引用基本情况(最后一个已计算)。我们称累加器为跟踪旧值的参数

    递归模板 常规递归函数如下所示:

    def regular(n)
        base_case
        computation
        return (result of computation) combined with (regular(n towards base case))
    
    要在尾部递归中转换它,我们:

    • 引入一个携带累加器的辅助函数
    • 在主函数中运行helper函数,将累加器设置为基本情况
    看:

    你的例子是: 我是这样做的:

    def BreakWords(glob):
        def helper(word, glob, acc_1, acc_2):
            if len(word) == 0 and len(glob) == 0:
                if not acc_1:
                    return None
                return acc
            if len(word) == 0:
                word = glob.pop[0]
                acc_2 = 0
    
            if IsWord(word.substring[:acc_2]):
                acc_1.append(word[:acc_2])
                return helper(word[acc_2 + 1:], glob, acc_1, acc_2 + 1)
    
            return helper(word[acc_2 + 1:], glob, acc_1, acc_2 + 1)
    
        return helper("", glob, [], 0)
    
    为了消除您所做的for语句,我使用2个累加器执行了递归助手函数。一个用于存储结果,另一个用于存储我当前尝试的位置

    尾部调用优化 因为没有状态存储在尾部调用堆栈的非边界案例中,所以它们并不重要。然后,一些语言/解释器用新的堆栈替换旧的堆栈。因此,由于没有堆栈帧限制调用的数量,尾部调用的行为就像for循环一样

    但不幸的是,Python并不是这些情况之一。当堆栈大于1000时,将出现运行时错误。
    认为由于尾部调用优化(由错误抛出的帧引起)而导致的调试目的的清晰性比特性更重要。真可惜。Python有这么多很酷的功能性东西,而且尾部递归在其上会很棒://

    您知道Python不会优化尾部递归(一般来说也不会优化尾部调用)?对于某些算法,您还需要在别处存储一些数据。我希望如果我能将其转换为尾部递归,那么将其转换为迭代就很容易了。一般来说,将递归函数转换为尾部递归函数的方法是传递累加器。在这种情况下,唯一的困难是递归调用位于迭代循环中。我认为直接将其转换为迭代会容易得多。这是一个可以接受的答案吗?@abarnert,我想是的。这意味着我必须手动实现回溯。我希望有更聪明的方法。@kirakun:好吧,你可以把迭代循环变成第二级递归,然后把两个函数放在一起,然后把结果函数转换成尾部递归,然后将其转换为迭代……我只是认为这不会比添加手动状态堆栈更容易实现或理解。
    def tail(n):
        def helper(n, accumulator):
            if n == base case:
                return accumulator
            computation
            accumulator = computation combined with accumulator
            return helper(n towards base case, accumulator)
        helper(n, base case)
    
    def BreakWords(glob):
        def helper(word, glob, acc_1, acc_2):
            if len(word) == 0 and len(glob) == 0:
                if not acc_1:
                    return None
                return acc
            if len(word) == 0:
                word = glob.pop[0]
                acc_2 = 0
    
            if IsWord(word.substring[:acc_2]):
                acc_1.append(word[:acc_2])
                return helper(word[acc_2 + 1:], glob, acc_1, acc_2 + 1)
    
            return helper(word[acc_2 + 1:], glob, acc_1, acc_2 + 1)
    
        return helper("", glob, [], 0)