Reputation: 105
I want to write a function called print_iterator_explicit() which prints all of the items in a linked list by making use of the Iterator. However, you are not permitted to use the standard "for ... in" loop syntax - instead you must create the Iterator object explicitly, and print each item by calling the next() method
Use the iter() and next() methods in your function definition - and remember to handle the StopIteration exception!
Below is what I've tried, but the print_iterator_explicit seems to have some problem which I can only print the first element but not the whole list.
class LinkedListIterator:
def __init__(self, head):
self.current = head
def __next__(self):
if self.current == None:
raise StopIteration
else:
item = self.current.get_data()
self.current = self.current.get_next()
return item
class LinkedList:
def __init__(self):
self.head = None
def __iter__(self):
return LinkedListIterator(self.head)
def add(self, item):
new_node = Node(item)
new_node.set_next(self.head)
self.head = new_node
def print_iterator_explicit(items):
it = items.__iter__()
print(it.__next__())
Upvotes: 8
Views: 15434
Reputation: 188
You can use the yield
keyword to make a generator so you dont have to implement __next__()
class LinkedList:
def __init__(self):
self.head = None
def __iter__(self):
curNode = self.head
while curNode:
yield curNode.value
curNode = curNode.nextNode
def add(self, item):
new_node = Node(item)
new_node.set_next(self.head)
self.head = new_node
And in your print_iterator_explicit
function you can do it like this
def print_iterator_explicit(items):
iterator = iter(ll)
while True:
try:
print(next(iterator))
except StopIteration:
break
Check out this link for more information on iterators and generators: Iterators and generators
A little side note: your head
variable is behaving like a tail
. In a linked list the first node is called the head and the last is called the tail
Upvotes: 6
Reputation: 31
Instead of a helper class, I found that a helper function is a bit cleaner:
class ListNode():
def __init__(self, val):
self.val = val
self.next = None
def __str__(self):
return ' -> '.join(str(v) for v in self)
def __iter__(self):
return ListIterator(self)
def ListIterator(head):
while head:
yield head.val
head = head.next
Upvotes: 1
Reputation: 915
Just as mentioned by @abarnert , you always need a __iter__
method for the iterator class.
class LinkedListIterator:
def __init__(self, head):
self.current = head
def __iter__(self):
return self
def __next__(self):
if not self.current:
raise StopIteration
else:
item = self.current.get_data()
self.current = self.current.get_next()
return item
class LinkedList:
def __init__(self):
self.head = None
def __iter__(self):
return LinkedListIterator(self.head)
def add(self, item):
new_node = Node(item)
new_node.set_next(self.head)
self.head = new_node
Now that your class is iterable, you can use "for...in" loop:
test_list = LinkedList()
test_list.add(1)
test_list.add(2)
test_list.add(3)
for item in test_list:
print(item)
Please check the tutorial here.
Upvotes: 7
Reputation: 366213
To iterate all of the items, you need some kind of loop—whether explicit or implicit.
You’re not allowed to use for val in items:
, which is the most obvious solution, but you can do a loop with while
:
it = iter(items)
while True:
try:
print(next(it))
except StopIteration:
break
This is, in fact, very close to what a for
loop does under the covers.
Or you can loop implicitly, say, with the list
constructor—or, even better, just by splatting the iterator:
print(*items, sep='\n')
(Of course if this is a homework assignment, that's the kind of answer that will get you an F if the teacher thinks you don't understand why it works, but maybe an A if you can explain it…)
Or you can use recursion:
def printemall(items):
print(next(items))
printemall(items)
try:
printemall(iter(items))
except StopIteration:
pass
Upvotes: 2