[英]Why does this function print a Linked List backwards?
我正在研究Downey的《如何像计算机科学家一样思考》,并且对他的Linked List的 print_backward()函数有疑问。
首先,这是Downey在Python中的链表的实现:
class Node:
#initialize with cargo (stores the value of the node)
#and the link. These are set to None initially.
def __init__(self, cargo = None, next = None):
self.cargo = cargo
self.next = next
def __str__(self):
return str(self.cargo)
我们为此类提供以下货物和链接值:
#cargo
node1 = Node('a')
node2 = Node('b')
node3 = Node('c')
#link them
node1.next = node2
node2.next = node3
要打印链接列表,我们使用Downey的另一个功能。
def printList(node):
while node:
print node,
node = node.next
>>>printList(node1)
>>>a b c
一切都很简单。 但是我不明白以下函数中的递归调用如何允许人们向后打印链表。
def print_backward(list):
if list == None : return
print_backward(list.next)
print list,
>>>print_backward(node1)
>>>c b a
不会将“ list.next”用作print_backward的值只是给您“ bc”吗?
注意:以下几位人士指出,此功能的设计不正确,因为给定任何列表,我们都无法表明它总是可以到达基本情况。 唐尼还在同一章的后面指出了这个问题。
def print_backward(list):
if list == None : return
print_backward(list.next)
print list,
不会将“ list.next”用作print_backward的值只是给您“ bc”吗?
没有; 想象一下当a-> b-> c传递给print_backward时会发生什么:
“ [bc]”传递给print_backward,然后打印“ a”。 但是在打印“ a”之前,“ print_backward”会自行调用。 所以:
在前向打印版本中,它将在执行递归调用之前打印每个节点。 在向后打印版本中,它将在执行递归调用后打印每个节点。
这不是偶然的。
这两个函数都会递归直到到达列表的末尾。 区别在于在此过程中还是之后进行打印。
函数调用使用堆栈,这是一种后进先出的数据结构,可以记住进行函数调用时计算机在哪里执行代码。 以一顺序放在堆栈中的内容以相反的顺序出现。 因此,递归以原始调用的相反顺序“解开”。 打印在展开过程中进行,即在每个递归调用完成之后进行。
如果list不是None,它将调用print_backward,然后打印列表的第一个成员。 扩展,这自然是会发生的事情。 您会看到当呼叫开始返回时,先打印“ c”,然后打印“ b”,再打印“ a”。
实际打印列表时,它会打印第一个节点
print_backward(list='a','b','c')
print_backward(list='b','c')
print_backward(list='c')
print_backward(list=None)
list is None, so return
print 'c'
print 'b','c'
print 'a','b','c'
有时,我发现将递归视为仅按一定顺序构造要调用的列表就容易了。 随着函数的继续,它将建立一堆调用,直到最终到达基本情况为止。 基本情况是不需要进一步分解程序的情况; 在此函数中,基本情况是没有任何可打印的内容,在这种情况下,我们无需执行return
就可以离开。
当我们展开函数调用的递归堆栈时,很酷的事情通常发生在返回的路上。 当前,已在列表的每个元素上调用print_backward
,它现在将“展开”,首先完成最近的调用,最后完成较早的调用。 这意味着,当您在最后一个元素上调用print_backward
创建的“实例”是第一个要完成的元素,因此最后一个元素是要打印的第一个元素,其后是倒数第二个,倒数第三个等等。 ,直到原始功能最终退出。
看一下发生了什么的这种表示形式:
print_backward(node1) #first call to print_backward
print_backward(node2) #calls itself on next node
print_backward(node3) #calls itself on next node
print_backward(None) #calls itself on None. We can now start unwinding as this is the base case:
print Node3 #now the third invocation finishes...
print Node2 #and the second...
print Node1 #and the first.
虽然在较早的元素上首先调用该函数,但实际打印该元素的部分位于递归调用之后,因此,在递归调用完成之前,它实际上不会执行。 在这种情况下,这意味着print list
部分要等到所有后来的元素都先被打印(以相反的顺序)后才能执行,从而使您可以向后打印列表元素。 :D
它正在使用递归。 它一直向下“压缩”直到结束,然后在每次调用返回时打印每个元素。 由于第一个要打印的是最近一次调用的,因此它将向后打印列表。
否。有两种递归:
null
,然后向后处理列表)。 每个函数调用都被压入堆栈,以供以后处理。 在您的示例中,该函数堆叠为'a'->'b'->'c'->null
,然后在弹出堆叠时,作者通过向后打印来表明:`if null return:print'c' ->打印'b'->打印'a' 在您的情况下,作者仅演示了递归的不同概念,并使用该概念将列表向后打印。
您的节点如下所示:
node1 node2 node3
'a' => 'b' => 'c' => None
在第一次调用print_backward
,变量list
的值为'a'
,随后对print_backward
调用进一步向下移动了一行。 请注意,他们没有打印任何东西,直到你打中后卫( None
),此时,事情就从后到前的印刷print_backward
收到节点'c'
必须在返回之前print_backward
收到节点'b'
可以打印(因为print语句在函数调用之后)等等。
虽然我认识到这是别人的代码,但是这里有一些不好的做法-最好在您学习时而不是以后告诉您。 首先,不要使用list
作为变量名,因为它是python中内置函数/类型的名称。 其次, if obj == None
更好地完成if obj == None
的相等性测试,最后,让您的类继承自object
( class node(object):
if obj is None
一个好主意,因为这使它成为新样式的类。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.