简体   繁体   English

为什么此功能向后打印链接列表?

[英]Why does this function print a Linked List backwards?

I'm working through Downey's How To Think Like a Computer Scientist and I have a question regarding his print_backward() function for Linked List . 我正在研究Downey的《如何像计算机科学家一样思考》,并且对他的Linked List的 print_backward()函数有疑问。

First, here's Downey's implementation of a Linked List in Python: 首先,这是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)

We give this class the following cargo and link values: 我们为此类提供以下货物和链接值:

 #cargo
 node1 = Node('a')
 node2 = Node('b')
 node3 = Node('c')
 #link them 
 node1.next = node2
 node2.next = node3

To print the linked list, we use another of Downey's functions. 要打印链接列表,我们使用Downey的另一个功能。

def printList(node):
     while node:
         print node,
         node = node.next

 >>>printList(node1)
 >>>a b c

All very straightforward. 一切都很简单。 But I don't understand how the recursive call in the following function allows one to print the linked list backwards. 但是我不明白以下函数中的递归调用如何允许人们向后打印链表。

def print_backward(list):
     if list == None : return
     print_backward(list.next)
     print list,

>>>print_backward(node1)
>>>c b a

Wouldn't calling "list.next" as the value of print_backward simply give you "bc"? 不会将“ list.next”用作print_backward的值只是给您“ bc”吗?

Note: Several people below have pointed out that this function is badly designed since, given any list, we cannot show that it will always reach the base case. 注意:以下几位人士指出,此功能的设计不正确,因为给定任何列表,我们都无法表明它总是可以到达基本情况。 Downey also points out this problem later in the same chapter. 唐尼还在同一章的后面指出了这个问题。

def print_backward(list):
     if list == None : return
     print_backward(list.next)
     print list,

Wouldn't calling "list.next" as the value of print_backward simply give you "bc"? 不会将“ list.next”用作print_backward的值只是给您“ bc”吗?

No; 没有; picture what happens when a->b->c gets passed to print_backward: 想象一下当a-> b-> c传递给print_backward时会发生什么:

"[bc]" is passed to print_backward and then "a" is printed. “ [bc]”传递给print_backward,然后打印“ a”。 But "print_backward", before "a" is printed, calls itself. 但是在打印“ a”之前,“ print_backward”会自行调用。 So: 所以:

  • [ abc ] is not None, so b->c gets passed to print_backward [abc]不是None,所以b-> c被传递给print_backward
    • [ bc ] is passed to print_backward [bc]传递给print_backward
      • [ c] is passed to print_backward [c]传递给print_backward
      • None is passed to print_backward 没有任何内容传递给print_backward
        • which returns 哪个返回
      • and then "c" is printed 然后打印“ c”
    • and then "b" is printed 然后打印“ b”
  • and then "a" is printed 然后打印“ a”
  • quit. 退出。

In the forward-printing version, it prints each node before doing the recursive call. 在前向打印版本中,它将在执行递归调用之前打印每个节点。 In the backward-printing version, it prints each node after doing the recursive call. 在向后打印版本中,它将执行递归调用打印每个节点。

This is not coincidental. 这不是偶然的。

Both of the functions recurse until the end of the list is reached. 这两个函数都会递归直到到达列表的末尾。 The difference is whether printing happens during this process or afterward. 区别在于在此过程中还是之后进行打印。

Function calls use a stack, a last-in first-out data structure that remembers where the computer was executing code when the function call was made. 函数调用使用堆栈,这是一种后进先出的数据结构,可以记住进行函数调用时计算机在哪里执行代码。 What is put on in the stack in one order comes off in the opposite order. 以一顺序放在堆栈中的内容以相反的顺序出现。 Thus, the recursion is "unwound" in the reverse order of the original calls. 因此,递归以原始调用的相反顺序“解开”。 The printing occurs during the unwinding process, ie, after each recursive call has completed. 打印在展开过程中进行,即在每个递归调用完成之后进行。

If list isn't None, it calls print_backward, then prints the first member of the list. 如果list不是None,它将调用print_backward,然后打印列表的第一个成员。 Expanded, this is exssentially what happens. 扩展,这自然是会发生的事情。 You can see that when calls start returning, 'c' is printed, then 'b', then 'a'. 您会看到当呼叫开始返回时,先打印“ c”,然后打印“ b”,再打印“ a”。

It looks like when actually printing a list, it prints the first node 实际打印列表时,它会打印第一个节点

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'

Sometimes I find it easier to think of recursion as merely constructing a list of calls to be made in a certain order. 有时,我发现将递归视为仅按一定顺序构造要调用的列表就容易了。 As the function continues, it builds up a bunch of calls until it finally gets to the base case. 随着函数的继续,它将建立一堆调用,直到最终到达基本情况为止。 The base case is the situation where no further breaking down the of program is necessary; 基本情况是不需要进一步分解程序的情况; in this function, the base case is when there is nothing to print, in which case we just leave without doing anything with return . 在此函数中,基本情况是没有任何可打印的内容,在这种情况下,我们无需执行return就可以离开。

The cool stuff usually happens on the way back as we unwind the recursive stack of function calls. 当我们展开函数调用的递归堆栈时,很酷的事情通常发生在返回的路上。 currently, print_backward has been called on each element of the list, and it will now 'unwind', finishing the most recent calls first and the earlier calls last. 当前,已在列表的每个元素上调用print_backward ,它现在将“展开”,首先完成最近的调用,最后完成较早的调用。 This means that the 'instance' of print_backward created when you call it on the last element is the first one to finish, and thus the last element is the first one to be printed, followed by the second to last, third to last, etc., until the original function finally exits. 这意味着,当您在最后一个元素上调用print_backward创建的“实例”是第一个要完成的元素,因此最后一个元素是要打印的第一个元素,其后是倒数第二个,倒数第三个等等。 ,直到原始功能最终退出。

Take a look at this representation of what happened: 看一下发生了什么的这种表示形式:

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. 

While the function is called first on the earlier elements, the part that actually prints that element comes after the recursive call, so it won't actually execute until that recursive call finishes. 虽然在较早的元素上首先调用该函数,但实际打印该元素的部分位于递归调用之后,因此,在递归调用完成之前,它实际上不会执行。 In this case, that means that the print list part won't execute until all of the later elements have been printed first (in reverse order), thus giving you the list elements printed backwards. 在这种情况下,这意味着print list部分要等到所有后来的元素都先被打印(以相反的顺序)后才能执行,从而使您可以向后打印列表元素。 :D :D

It's using recursion. 它正在使用递归。 It "zips" all the way down until it gets to the end, then it prints every element as each call returns. 它一直向下“压缩”直到结束,然后在每次调用返回时打印每个元素。 Since the first one to get to print is the most recent called, it prints the list backwards. 由于第一个要打印的是最近一次调用的,因此它将向后打印列表。

No. There's two kinds of recursion: 否。有两种递归:

  1. Tail recursion: if there is nothing to do after the function returns except return its value. 尾递归:如果函数返回后无事可做,则返回其值。 Function calls are not stacked. 函数调用未堆叠。
  2. Recursion that finds the base case first (in this case, null , then backwardly processes the list). 递归首先查找基本情况(在这种情况下为null ,然后向后处理列表)。 Each function call is pushed into the stack, for later processing. 每个函数调用都被压入堆栈,以供以后处理。 In your exemple, the function is stacked as 'a'->'b'->'c'->null , then as the stack is popped, the author showed that by printing backwards: `if null return: print 'c' -> print 'b' -> print 'a' 在您的示例中,该函数堆叠为'a'->'b'->'c'->null ,然后在弹出堆叠时,作者通过向后打印来表明:`if null return:print'c' ->打印'b'->打印'a'

In your case, the author only demonstrated a different concept of recursion, and used that to print the list backwards. 在您的情况下,作者仅演示了递归的不同概念,并使用该概念将列表向后打印。

Your nodes look something like this: 您的节点如下所示:

node1 node2 node3
'a' => 'b' => 'c' => None

At the first call to print_backward , the variable list has the value 'a' , subsequent calls to print_backward move one further down the line. 在第一次调用print_backward ,变量list的值为'a' ,随后对print_backward调用进一步向下移动了一行。 Note that none of them print anything until you hit the guard ( None ) at which time, things get printed from the back to front as the print_backward that received node 'c' must return before the print_backward that received node 'b' can print (because the print statement is after the function call) and so on. 请注意,他们没有打印任何东西,直到你打中后卫( None ),此时,事情就从后到前的印刷print_backward收到节点'c'必须在返回之前print_backward收到节点'b'可以打印(因为print语句在函数调用之后)等等。

While I recognize that this is somebody else's code, there are a few things in here which are bad practice -- Best I tell you now while you're learning rather than later. 虽然我认识到这是别人的代码,但是这里有一些不好的做法-最好在您学习时而不是以后告诉您。 First, don't use list as a variable name since it is the name of a builtin function/type in python. 首先,不要使用list作为变量名,因为它是python中内置函数/类型的名称。 second the equality test if obj == None is better done by if obj is None , finally, it's always a good idea to have your classes inherit from object ( class node(object): ) as that makes it a new-style class. 其次, if obj == None更好地完成if obj == None的相等性测试,最后,让您的类继承自objectclass node(object): if obj is None一个好主意,因为这使它成为新样式的类。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM