我正在通过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的值只是给你“b c”?
注意:下面有几个人指出这个功能设计得很糟糕,因为在给定任何列表的情况下,我们无法证明它总能达到基本情况。唐尼也在同一章后面指出了这个问题。
答案 0 :(得分:2)
def print_backward(list):
if list == None : return
print_backward(list.next)
print list,
不会调用“list.next”作为print_backward的值只是给你“b c”吗?
没有;图片当a-> b-> c传递给print_backward时会发生什么:
“[b c]”传递给print_backward,然后打印“a”。 但是,在打印“a”之前,“print_backward”会调用自己。所以:
答案 1 :(得分:2)
在正向打印版本中,它会在执行递归调用之前打印每个节点。在向后打印版本中,它在执行递归调用后打印每个节点。
这不是巧合。
两个函数都会递归,直到到达列表末尾。不同的是在此过程中或之后是否发生打印。
函数调用使用堆栈,这是一种后进先出数据结构,可记住在进行函数调用时计算机执行代码的位置。在一个订单中堆叠的内容以相反的顺序出现。因此,递归以原始调用的相反顺序“展开”。打印发生在展开过程中,即每次递归调用完成后。
答案 2 :(得分:1)
如果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'
答案 3 :(得分:1)
有时我发现将递归视为仅构建按特定顺序进行的调用列表更容易。随着函数的继续,它会构建一堆调用,直到它最终到达基本情况。基本情况是不需要进一步破坏程序的情况;在这个函数中,基本情况是什么都没有打印,在这种情况下,我们只是离开而没有做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
答案 4 :(得分:0)
它正在使用递归。它一直“拉链”直到它结束,然后在每次调用返回时打印每个元素。由于第一个要打印的是最近调用的,它会向后打印列表。
答案 5 :(得分:0)
没有。有两种递归:
null
,然后向后处理列表)。每个函数调用都被推入堆栈,以便以后处理。在你的例子中,函数被堆叠为'a'->'b'->'c'->null
,然后当弹出堆栈时,作者通过向后打印显示:`if null return:print'c' - > print'b' - >打印'a'在您的情况下,作者只展示了不同的递归概念,并使用它来向后打印列表。
答案 6 :(得分:0)
您的节点看起来像这样:
node1 node2 node3
'a' => 'b' => 'c' => None
第一次调用print_backward
时,变量list
的值为'a'
,后续调用print_backward
会向下移动一行None
。请注意,在你击中警卫(print_backward
)之前,他们都不会打印任何东西,此时,事情会从后到前打印,因为收到节点'c'
的{{1}}必须在返回之前返回收到节点print_backward
的{{1}}可以打印(因为print语句在函数调用之后),依此类推。
虽然我认识到这是其他人的代码,但这里有一些不好的做法 - 我现在告诉你的是你学习的时候而不是以后。首先,不要使用'b'
作为变量名,因为它是python中内置函数/类型的名称。第二,list
更好地完成了等级测试if obj == None
,最后,让你的类继承自if obj is None
(object
)总是一个好主意,因为它使它成为一个新的式班。