8

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__())
3
  • First, you almost never want to call dunder methods like __iter__ and __next__. Use it = iter(items) and next(it). Also, you should check spam is None, or sometimes not spam, but never spam == None except in very rare cases. Commented Apr 25, 2018 at 23:08
  • You're only calling print once, why would you expect it to print multiple times? You're still going to need a loop. If you can't use a for loop, (a silly restriction, but whatever) that only leaves while loops Commented Apr 25, 2018 at 23:09
  • One last thing: An iterator class should almost always have an __iter__ method that just does a return self. Commented Apr 25, 2018 at 23:38

4 Answers 4

8

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.

Sign up to request clarification or add additional context in comments.

Comments

6

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

Comments

2

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

1 Comment

Cheers! That would be helpful
1

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

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.