简体   繁体   English

双链接的前哨列表中的__next__和__iter__方法

[英]__next__ and __iter__ methods in doubly linked sentinel list

I'm working on a doubly linked list class right now and I'm running into trouble with my next and iter methods. 我现在正在研究一个双向链表类,而我的next和iter方法遇到了麻烦。 This is for a project for my class that I've already turned in and now just want to understand how to actually fix it to make it useful. 这是我上课的一个项目,现在只想了解如何实际修复它以使其有用。

What I want my code to do is set a current pointer, starting at the header, and then continue advancing through until instructed to terminate or when it reaches the trailer. 我要我的代码执行的操作是设置一个当前指针,从头开始,然后继续前进直到指示终止或到达预告片为止。 I want to access the value stored at each node. 我想访问存储在每个节点上的值。 The node class is a subclass of the main linked list class. 节点类是主链接列表类的子类。 Here is the code I have. 这是我的代码。 My problem appears when I call upon my methods (posting my append method); 当我调用我的方法(发布我的append方法)时出现了我的问题; the current pointer is not recognized. 当前指针无法识别。 Any ideas on how to fix this? 有想法该怎么解决这个吗?

class Linked_List:

    class __Node:
        def __init__(self, val):
          self.val = val
          self.size = 0

    def __init__(self):
        self.header = Linked_List.__Node('header')
        self.trailer = Linked_List.__Node('trailer')
        self.header.next = self.trailer
        self.trailer.prev = self.header
        self.size = 0
        self.current = self.header
        self.current.next = self.trailer

    def __iter__(self):
        self.current = self.header
        return self


    def __next__(self):
        if self.current == self.trailer:
            raise StopIteration
        result = self.Linked_List.__Node[self.current]
        self.current = self.current.next
        return result

    def append(self, val):
        new_node = Linked_List.__Node(val)

        if self.header.next is self.trailer:
            self.header.next = new_node
            self.trailer.prev = new_node
            self.current = self.header
        else:
            while self.current is not self.trailer:
                self.current = self.current.next
            self.current.next = new_node
            new_node.next = self.trailer
            new_node.prev = self.current
        self.size += 1

I'm newer to python (and coding in general) so any advice would be amazing. 我是python的新手(和一般而言的编码),所以任何建议都很棒。

Your code has multiple issues, which get apparent as you attempt to use it. 您的代码有多个问题,当您尝试使用它时,这些问题会很明显。 Let's assume the following code to test it: 让我们假设以下代码对其进行测试:

l = Linked_List()
l.append('foo')
l.append('bar')
l.append('baz')

print([x.val for x in l])

AttributeError: '__Node' object has no attribute 'next' AttributeError:'__Node'对象没有属性'next'

First issue: Your __Node type does not have fields for next and prev : 第一个问题:您的__Node类型没有nextprev字段:

class __Node:
    def __init__(self, val):
      self.val = val
      self.size = 0
      self.prev = None
      self.next = None

AttributeError: 'NoneType' object has no attribute 'next' AttributeError:“ NoneType”对象没有属性“ next”

Second issue: next is not always being filled for appended nodes. 第二个问题: next并不总是为附加节点填充。 In one of your paths in append , you do not set next and prev of the new node: append其中一个路径中,没有设置新节点的nextprev

def append(self, val):
    new_node = Linked_List.__Node(val)

    if self.header.next is self.trailer:
        # set the attributes on new_node
        new_node.prev = self.header
        new_node.next = self.trailer

        self.header.next = new_node
        self.trailer.prev = new_node
        self.current = self.header
    # …

AttributeError: 'Linked_List' object has no attribute 'Linked_List' AttributeError:“ Linked_List”对象没有属性“ Linked_List”

Third issue: No idea what you were trying to do in __next__ there. 第三个问题:不知道您想在__next__那里做什么。 You should simply access self.current there: 您应该只self.current那里访问self.current

def __next__(self):
    if self.current == self.trailer:
        raise StopIteration
    result = self.current
    self.current = self.current.next
    return result

Once we have fixed all that, we have a code that runs successfully. 修复所有问题后,我们便拥有了可以成功运行的代码。 But we only get the following output: ['header', 'foo'] . 但是我们只得到以下输出: ['header', 'foo'] Of course, that's not what we want. 当然,这不是我们想要的。

The reason this happens is because the actual order of the items is the following: 发生这种情况的原因是,这些项目的实际顺序如下:

header
foo
trailer
baz
trailer

(Yes, there is a recursion) So apparently, the append did not work correctly after all. (是的,有一个递归)因此,显然, append毕竟不能正常工作。 If you just append two elements, you can see that the element is being added after the trailer element. 如果你只是追加两个元素,你可以看到,该元素拖车元素之后添加。 This means that self.current does hit the trailer element in the append loop after all: 这意味着self.current会在append循环中击中trailer元素:

while self.current is not self.trailer:
    self.current = self.current.next

And if you look at it, it makes sense that this happens: self.current is updated first, and then the check is made to eventually cancel the loop. 而且,如果您看一下它,就很可能会发生这种情况: self.current更新self.current ,然后进行检查以最终取消循环。 At that time self.current is self.trailer . 那时self.currentself.trailer So we should check self.current.next instead: 因此,我们应该改为检查self.current.next

while self.current.next is not self.trailer:
    self.current = self.current.next

Once we have that fixed, we get the following output: ['header', 'foo', 'bar', 'baz'] . 修复此问题后,我们将获得以下输出: ['header', 'foo', 'bar', 'baz'] That's almost what we would like to see. 那几乎就是我们想要看到的。 All we need to do now is to skip the header element as well. 我们现在要做的就是也跳过header元素。 We do that by simply starting from the element after the header: 我们可以简单地从标头后面的元素开始:

def __iter__(self):
    self.current = self.header.next
    return self

And then it works. 然后工作。


This is all it takes to get your code running. 这就是使您的代码运行所需的全部。 However , I would generally advise against this approach. 但是 ,我通常建议不要使用这种方法。 You are storing iteration state inside the list, which is very fragile. 您将迭代状态存储在列表中,这非常脆弱。 You should really have this state as local as you possibly can. 您实际上应该尽可能地将此状态设置为局部状态。

In particular, the linked list does not need to be both enumerable and an enumerator . 特别是,链表不必既可枚举又可枚举 Implementing __iter__ does the former, implementing __next__ does the latter. 前一个实现__iter__ ,后一个实现__next__ Enumerable means “you can iterate this thing” while the enumerator is the thing that is performing the iteration and which has the iteration state. 可枚举的意思是“您可以迭代此事物”,而枚举器是执行迭代并具有迭代状态的事物。

Try moving the iteration state off the linked list, by making it only enumerable and not an enumerator. 尝试通过使迭代状态仅可枚举而不是枚举数来将其从链表中移出 To do this, add a LinkedListEnumerator type that has a reference to your list and keeps track of the current element: 为此,添加一个LinkedListEnumerator类型,该类型具有对列表的引用并跟踪当前元素:

class LinkedListEnumerator:
    def __init__ (self, lst):
        self.lst = lst
        self.current = lst.header.next

    def __iter__ (self):
        return self

    def __next__ (self):
        if self.current == self.lst.trailer:
            raise StopIteration
        result = self.current
        self.current = self.current.next
        return result

You can then delete the __next__ method in your linked list, and replace the __iter__ method by the following: 然后,您可以在链接列表中删除__next__方法,并用以下替换__iter__方法:

def __iter__(self):
    return LinkedListEnumerator(self)

And then you no longer have the state in the linked list. 然后,您不再在链接列表中拥有状态。 (At this point, you should also make current a local variable in append and get rid of self.current completely) (在这一点上,您还应该使current成为append局部变量,并完全摆脱self.current

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

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