【问题标题】:Destructive Stack Iteration破坏性堆栈迭代
【发布时间】:2018-05-23 15:48:29
【问题描述】:

这是我的 Stack 实现。

class Stack:
    def __init__(self):
        self.head = None
        self.size = 0

    def push(self, item):
        node = Node(item)
        if not self.head:
            self.head = node
        else:
            node.next = self.head
            self.head = node
        self.size += 1

    def pop(self):
        if self.size == 0:
            raise ValueError('Popping off an empty stack!')
        item = self.head.val
        self.head = self.head.next
        return item

    def peek(self):
        if self.size == 0:
            raise ValueError('Peeking into an empty stack!')
        return self.head.val

    def __iter__(self):
        return self

    def __next__(self):
        if self.head:
            curr = self.head
        else:
            raise StopIteration()
        self.head = self.head.next
        return curr.val

class Node:
    def __init__(self, val):
        self.val = val
        self.next = None


if __name__ == '__main__':
    stack = Stack()
    stack.push(12)
    stack.push(13)
    stack.push(9)
    for item in stack:
        print(item)
    print(stack.peek())

问题在于迭代。迭代是破坏性的,因此在迭代结束时调用 peek 会引发错误。

return self.head.val AttributeError: 'NoneType' object has no attribute 'val' 如何使迭代不具有破坏性?

【问题讨论】:

  • 感谢您的接受,但实际上,您可能应该接受 tim 或 Daniel 的解决方案,因为它比我的更强大(尽管我想我的更容易理解)。
  • Python 已经带有堆栈类型collections.deque。它很可能会提供您需要的所有功能。

标签: python stack


【解决方案1】:

一种简单的方法来为您的 Stack 提供一个可用于迭代的替代头。我还添加了一个__len__ 方法来返回堆栈的大小。

class Stack:
    def __init__(self):
        self.head = None
        self.size = 0

    def __len__(self):
        return self.size

    def push(self, item):
        node = Node(item)
        if not self.head:
            self.head = node
        else:
            node.next = self.head
            self.head = node
        self.size += 1

    def pop(self):
        if self.size == 0:
            raise ValueError('Popping off an empty stack!')
        item = self.head.val
        self.head = self.head.next
        return item

    def peek(self):
        if self.size == 0:
            raise ValueError('Peeking into an empty stack!')
        return self.head.val

    def __iter__(self):
        self.top = self.head
        return self

    def __next__(self):
        if self.top:
            curr = self.top
        else:
            raise StopIteration()
        self.top = self.top.next
        return curr.val

class Node:
    def __init__(self, val):
        self.val = val
        self.next = None    

if __name__ == '__main__':
    stack = Stack()
    stack.push(12)
    stack.push(13)
    stack.push(9)
    print('Size', len(stack))
    for item in stack:
        print(item)
    print(stack.peek())
    stack.push(42)
    print('Size', len(stack))
    for item in stack:
        print(item)

输出

Size 3
9
13
12
9
Size 4
42
9
13
12

self.top = None 添加到__init__ 可能是一个好主意,尽管这不是绝对必要的。您可能希望将其标记为带有前导下划线的私有名称:self._top

正如 timgeb 在 cmets 中暗示的那样,这种方法有点脆弱,因为我们一次只能在堆栈上执行一次迭代,因为我们只有一个 self.top


顺便说一句,您可以稍微优化 push 方法:

def push(self, item):
    node = Node(item)
    if self.head:
        node.next = self.head
    self.head = node
    self.size += 1

【讨论】:

  • 现在尝试在两个嵌套的for 循环中遍历同一个堆栈。 :)
  • @timgeb 嘿,我说它简单,我没说它很健壮。 :D
  • 您的回答仍然有助于了解为什么标准的 python 可迭代对象(如列表)本身不是迭代器,所以 +1 :)
  • @timgeb 你和丹尼尔几乎同时发布了一个优秀的解决方案,所以我想你们都值得一分。
【解决方案2】:

问题在于您没有在 iterable 堆栈本身和 __iter__ 应该返回的 iterator 之间做出区别。 __next__ 应该在所述迭代器上调用,而不是在 Stack 本身上。

我提出以下解决方案:

class StackIterator:
    def __init__(self, stack):
        self.head = stack.head

    def __iter__(self):
        return self

    def __next__(self):
        if not self.head:
            raise StopIteration

        current = self.head
        self.head = self.head.next

        return current.val

去掉Stack中的__next__,调整__iter__为:

def __iter__(self):
    return StackIterator(self)

演示:

>>> stack = Stack()
>>> stack.push(12)
>>> stack.push(13)
>>> stack.push(9)
>>> 
>>> for x in stack:
...     print(x)
... 
9
13
12
>>> stack.peek()
9

【讨论】:

    【解决方案3】:

    因为您可以在同一个堆栈上拥有多个迭代器,__iter__ 必须返回一个迭代器对象,用于迭代堆栈:

    class Stack:
        def __init__(self):
            self.head = None
            self.size = 0
    
        def push(self, item):
            node = Node(item)
            if self.head:
                node.next = self.head
            self.head = node
            self.size += 1
    
        def pop(self):
            if self.size == 0:
                raise ValueError('Popping off an empty stack!')
            item = self.head.val
            self.head = self.head.next
            return item
    
        def peek(self):
            if self.size == 0:
                raise ValueError('Peeking into an empty stack!')
            return self.head.val
    
        def __iter__(self):
            return StackIterator(self.head)
    
    class StackIterator:
        def __init__(self, head):
            self.head = head
    
        def __iter__(self):
            return self
    
        def __next__(self):
            if not self.head:
                raise StopIteration()
            item = self.head.val
            self.head = self.head.next
            return item
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2019-03-24
      • 1970-01-01
      • 2011-04-21
      • 2011-11-01
      • 2011-01-06
      • 1970-01-01
      • 2012-09-24
      相关资源
      最近更新 更多