Computer Science 112 Fundamentals of Programming II Iterators

  • Slides: 31
Download presentation
Computer Science 112 Fundamentals of Programming II Iterators

Computer Science 112 Fundamentals of Programming II Iterators

The for Loop for value in <any collection>: <do something with value> Allows the

The for Loop for value in <any collection>: <do something with value> Allows the programmer to iterate through all of the values in a collection (or any other iterable object)

The for Loop for value in <any collection>: <do something with value> Users: list(<any

The for Loop for value in <any collection>: <do something with value> Users: list(<any collection>) # builds a list from the collection

The for Loop for value in <any collection>: <do something with value> Users: list(<any

The for Loop for value in <any collection>: <do something with value> Users: list(<any collection>) # builds a list from the collection sum(<any collection of numbers>)

The for Loop for value in <any collection>: <do something with value> Users: list(<any

The for Loop for value in <any collection>: <do something with value> Users: list(<any collection>) # builds a list from the collection sum(<any collection of numbers>) min(<any collection of comparables>)

The for Loop for value in <any collection>: <do something with value> Users: list(<any

The for Loop for value in <any collection>: <do something with value> Users: list(<any collection>) # builds a list from the collection sum(<any collection of numbers>) min(<any collection of comparables>) map(<a function of one arg>, <any collection>)

The for Loop for value in <any collection>: <do something with value> Users: list(<any

The for Loop for value in <any collection>: <do something with value> Users: list(<any collection>) # builds a list from the collection sum(<any collection of numbers>) min(<any collection of comparables>) map(<a function of one arg>, <any collection>) filter(<a predicate of one arg>, <any collection>)

The for Loop for value in <any collection>: <do something with value> Users: list(<any

The for Loop for value in <any collection>: <do something with value> Users: list(<any collection>) # builds a list from the collection sum(<any collection of numbers>) min(<any collection of comparables>) map(<a function of one arg>, <any collection>) filter(<a predicate of one arg>, <any collection>) <a value> in <any collection>

The Benefits of a for Loop class Array. Bag(object): DEFAULT_CAPACITY = 10 def __init__(self,

The Benefits of a for Loop class Array. Bag(object): DEFAULT_CAPACITY = 10 def __init__(self, source. Collection = None): self. _items = Array(Array. Bag. DEFAULT_CAPACITY) self. _size = 0 if source. Collection: for item in source. Collection: self. add(item) Here we assume that source. Collection is iterable bag = Array. Bag(range(1, 11))

The Benefits of a for Loop class Linked. Bag(object): def __init__(self, source. Collection =

The Benefits of a for Loop class Linked. Bag(object): def __init__(self, source. Collection = None): self. _items = None self. _size = 0 if source. Collection: for item in source. Collection: self. add(item) Here we assume that source. Collection is iterable bag = Linked. Bag(range(1, 11))

The Benefits of a for Loop class Array. Sorted. Bag(object): def __init__(self, source. Collection

The Benefits of a for Loop class Array. Sorted. Bag(object): def __init__(self, source. Collection = None): self. _items = Array(Array. Sorted. Bag. DEFAULT_CAPACITY) self. _size = 0 if source. Collection: for item in source. Collection: self. add(item) Here we assume that source. Collection is iterable bag = Array. Sorted. Bag([44, 33, 55, 22, 10])

The Benefits of a for Loop class Array. Bag(object): def __add__(self, other): """Returns a

The Benefits of a for Loop class Array. Bag(object): def __add__(self, other): """Returns a new bag containing the contents of self and other. """ result = Array. Bag(self) for item in other: result. add(item) return result Here we assume that self and other are iterable b 3 = b 1 + b 2

The Benefits of a for Loop class Array. Set(object): def __eq__(self, other): """Returns True

The Benefits of a for Loop class Array. Set(object): def __eq__(self, other): """Returns True if self equals other, or False otherwise. """ if self is other: return True if type(self) != type(other) or len(self) != len(other): return False for item in self: if not item in other: return False return True Here we assume that self and other are iterable b 1 == b 2

The Benefits of a for Loop class Array. Bag(object): def __str__(self): """Returns the string

The Benefits of a for Loop class Array. Bag(object): def __str__(self): """Returns the string representation of self. """ return "{" + ", ". join(map(str, self)) + "}" Here we assume that self is iterable

Loop Patterns and Iterators for i in range(some. Integer): <do something with i> for

Loop Patterns and Iterators for i in range(some. Integer): <do something with i> for value in some. Collection: <do something with value> When the PVM sees a for loop, it evaluates the second operand of in and then calls the iter function on its value The code in the iter function repeatedly fetches the next value in the second operand binds it to the first operand After each fetch operation, the code in the loop body is run

Two Loop Patterns for i in range(some. Integer): <do something with i> for value

Two Loop Patterns for i in range(some. Integer): <do something with i> for value in some. Collection: <do something with value> Thus, the second operand of in must include an __iter__ method in its class If you want your collection to be iterable, you must define an __iter__ method

Implementing an Iterator class Array(object): def __init__(self, capacity, fill. Value = None): self. _items

Implementing an Iterator class Array(object): def __init__(self, capacity, fill. Value = None): self. _items = list() for count in range(capacity): self. _items. append(fill. Value) def __iter__(self): return iter(self. _items) The __iter__ method of the Array class simply returns the result of calling iter on its underlying list

Array. Bag Iterator: A Nice Try class Array. Bag(object): DEFAULT_CAPACITY = 10 def __init__(self,

Array. Bag Iterator: A Nice Try class Array. Bag(object): DEFAULT_CAPACITY = 10 def __init__(self, source. Collection = None): self. _items = Array(Array. Bag. DEFAULT_CAPACITY) self. _size = 0 if source. Collection: for item in source. Collection: self. add(item) def __iter__(self): return iter(self. _items) What’s wrong with this code?

Designing an Iterator • The __iter__ method must initialize and maintain a cursor, which

Designing an Iterator • The __iter__ method must initialize and maintain a cursor, which locates the current item • __iter__’s loop quits when the cursor goes off the end of the collection • The body of the loop yields the current item and advances the cursor

Implementing an Iterator class Array. Bag(object): DEFAULT_CAPACITY = 10 def __init__(self, source. Collection =

Implementing an Iterator class Array. Bag(object): DEFAULT_CAPACITY = 10 def __init__(self, source. Collection = None): self. _items = Array(Array. Bag. DEFAULT_CAPACITY) self. _size = 0 if source. Collection: for item in source. Collection: self. add(item) def __iter__(self): cursor = 0 while cursor < len(self): yield self. _items[cursor] cursor += 1

Running an Iterator def __iter__(self): cursor = 0 while cursor < len(self): yield self.

Running an Iterator def __iter__(self): cursor = 0 while cursor < len(self): yield self. _items[cursor] cursor += 1 for item in bag: <do something with item> collection iterator D D D The app running the for loop runs in a different process from the iterator’s process

Running an Iterator def __iter__(self): cursor = 0 while cursor < len(self): yield self.

Running an Iterator def __iter__(self): cursor = 0 while cursor < len(self): yield self. _items[cursor] cursor += 1 for item in bag: <do something with item> collection iterator D D D The app running the for loop runs in a different process from the iterator’s process

Running an Iterator def __iter__(self): cursor = 0 while cursor < len(self): yield self.

Running an Iterator def __iter__(self): cursor = 0 while cursor < len(self): yield self. _items[cursor] cursor += 1 for item in bag: <do something with item> collection iterator D D D The app running the for loop runs in a different process from the iterator’s process

Running an Iterator def __iter__(self): cursor = 0 while cursor < len(self): yield self.

Running an Iterator def __iter__(self): cursor = 0 while cursor < len(self): yield self. _items[cursor] cursor += 1 for item in bag: <do something with item> collection iterator D D D The app running the for loop runs in a different process from the iterator’s process

An Iterator for a Linked Structure • The cursor would be initialized to the

An Iterator for a Linked Structure • The cursor would be initialized to the head pointer (like a probe) • The loop quits when the cursor equals None • The value yielded is in the cursor’s node • The cursor is updated by setting it to the next node

Implementing an Iterator class Linked. Bag(object): def __init__(self, source. Collection = None): self. _items

Implementing an Iterator class Linked. Bag(object): def __init__(self, source. Collection = None): self. _items = None self. _size = 0 if source. Collection: for item in source. Collection: self. add(item) def __iter__(self): cursor = self. _items while cursor != None: yield cursor. data cursor = cursor. next

Iterator Should Be Read-Only for item in bag: bag. remove(item) Mutations in the context

Iterator Should Be Read-Only for item in bag: bag. remove(item) Mutations in the context of a for loop can cause the state of the underlying collection to become inconsistent with the state of the iterator

Iterator Should Be Read-Only for item in bag: bag. remove(item) # Will raise an

Iterator Should Be Read-Only for item in bag: bag. remove(item) # Will raise an exception bag. clear() # Preferred method We can arrange for the collection to track mutations so that the iterator can detect illicit ones and raise exceptions

Tracking Modifications class Linked. Bag(object): def __init__(self, source. Collection = None): self. _items =

Tracking Modifications class Linked. Bag(object): def __init__(self, source. Collection = None): self. _items = None self. _size = 0 self. _mod. Count = 0 # Tracks mutations if source. Collection: for item in source. Collection: self. add(item) def add(self, item): self. _items = Node(item, self. _items) self. _size += 1 self. _mod. Count += 1 Each mutator method increments the mod count

Detecting Modifications class Linked. Bag(object): def __init__(self, source. Collection = None): self. _items =

Detecting Modifications class Linked. Bag(object): def __init__(self, source. Collection = None): self. _items = None self. _size = 0 if source. Collection: for item in source. Collection: self. add(item) def __iter__(self): my. Mod. Count = self. _mod. Count cursor = self. _items while cursor != None: yield cursor. data if my. Mod. Count != self. _mod. Count: # Gotcha! raise Attribute. Error( "Cannot modify the backing store in a for loop") cursor = cursor. next

For Monday Equality Start Chapter 6

For Monday Equality Start Chapter 6