Python 为什么此函数会向后打印链接列表?
我正在研究唐尼的《如何像计算机科学家一样思考》,我有一个关于他的print_backward()函数的问题 首先,下面是Downey在Python中对链表的实现:Python 为什么此函数会向后打印链接列表?,python,linked-list,Python,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
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
为了打印链表,我们使用了唐尼的另一个函数
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,
>>>print_backward(node1)
>>>c b a
null
,然后反向处理列表)。每个函数调用都被推送到堆栈中,以便以后处理。在您的示例中,函数被堆叠为'a'->'b'->'c'->null
,然后当堆栈弹出时,作者通过向后打印显示:`if null return:print'c'->print'b'->print'a'在您的例子中,作者只演示了一个不同的递归概念,并使用它来向后打印列表。您的节点如下所示:
def print_backward(list):
if list == None : return
print_backward(list.next)
print list,
>>>print_backward(node1)
>>>c b a
node1 node2 node3
'a' => 'b' => 'c' => None
在第一次调用print\u backward
时,变量list
的值为'a'
,随后调用print\u backward
时,将该行再向下移动一个变量。请注意,在您点击防护装置(none
)之前,它们都不会打印任何内容。此时,将从后向前打印内容,因为接收节点'c'
的print\u backward
必须在接收节点'b'
可以打印之前返回(因为print语句在函数调用之后)等等
虽然我知道这是其他人的代码,但这里有一些不好的做法——最好是在你学习的时候告诉你。首先,不要使用
list
作为变量名,因为它是python中内置函数/类型的名称。其次,如果obj==None更好,那么等式测试er由完成如果obj是None
,最后,让类从对象继承总是一个好主意(类节点(对象):
)这使得它成为了一个新的样式类。如果list不是None,它会向后调用print\u,然后打印列表的第一个成员。展开后,这就是本质上发生的情况。您可以看到,当调用开始返回时,“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'
调用“list.next”作为print_backward的值不就是简单的“bc”吗
否;想象当a->b->c被传递到向后打印时会发生什么:
将“[b c]”传递到“向后打印”,然后打印“a”。
但在“a”被打印之前的“print_backward”会调用它自己。所以:
- [AB c]不是None,因此b->c被传递到向后打印
- [b c]被传递到向后打印
- [c]被传递到向后打印
- 未将任何内容传递给“向后打印”
- 返回
- 然后打印“c”
- 然后打印“b”
- 然后打印“a”
- 退出
有时我发现更容易将递归视为仅仅构造一个按特定顺序进行的调用列表。随着函数的继续,它会生成一系列调用,直到最终到达基本情况。基本情况是不需要进一步分解程序的情况;在这个函数中,基本情况是当没有任何东西要打印时,在这种情况下,我们只需离开,而不使用return
执行任何操作
当我们解开递归函数调用堆栈时,通常会在返回的过程中发生一些很酷的事情。目前,列表中的每个元素都调用了print\u backward
,现在它将“unwind”,首先完成最近的调用,最后完成前面的调用。这意味着print\u 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.
当函数首先在前面的元素上调用时,实际打印该元素的部分在递归调用之后,因此在该递归调用完成之前,它不会实际执行。在这种情况下,这意味着打印列表
部分在后面的所有元素都被首先打印之前不会执行(按相反的顺序),从而为您提供向后打印的列表元素。:D在正向打印版本中,它在执行递归调用之前打印每个节点。在反向打印版本中,它在执行递归调用之后打印每个节点
这并非巧合
这两个函数都会递归,直到到达列表的末尾。区别在于打印是在此过程中进行还是之后进行
函数调用使用堆栈,这是一种后进先出的数据结构,可以记住在进行函数调用时计算机在何处执行代码