2017-12-09 9 views
3

これは私のスタック実装です。破壊的なスタック反復

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()) 

この問題は繰り返しです。反復は破壊的であり、反復の終了時にピークを呼び出すとエラーがスローされます。

return self.head.val AttributeError: 'NoneType' object has no attribute 'val' 反復を非破壊的にするにはどうすればよいですか?スタックを反復し

+0

おかげで、それは私のものよりも堅牢なので、本当に、あなたはおそらく、ティムやダニエルのいずれかのソリューションを受け入れる必要があります(ただし:


ところで、あなたは少しそのpush方法を最適化することができます私のことを少し理解しやすいと思います)。 –

答えて

2

これを実行するための簡単な方法あなたのスタックに別の頭を与える反復処理に使用できます。また、__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 

それは、__init__self.top = Noneを追加するために、おそらく良いアイデアです。そしてそれを先頭のアンダースコアのプライベートネームとしてマークすることもできます:self._top

timgebはコメントで暗示しているように、このアプローチは少し壊れやすいものです。なぜなら、我々はただ1つのself.topしか持っていないからです。私は受け入れるための

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

2つのネストされた 'for'ループで同じスタックをループしてみてください。 :) – timgeb

+0

@timgebねえ、私はそれがとても簡単だと言った、私はそれが頑丈だとは言わなかった。 :D –

+0

あなたの答えは、リストのような標準的なpython iterablesがイテレータそのものではない理由を学ぶのにまだ役立ちます。+1: – timgeb

2

あなたが同じスタック上に複数の反復子を持つことができるため、__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 
2

問題はあなたが違いをしないということですiterableスタック自体とイテレータ__iter__が返されます。 __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 

がに__iter__Stack__next__を取り除くと調整しなさい:

は、私は、次の解決策を提案

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 
関連する問題