sequence[k] Patterns for Looping (“iterating”) - Rose

Sequences and Indexing
A sequence is a
type of thing that
represents a:
• Finite, ordered
collection of
things
• Indexed by nonnegative
integers.
For example:
•
A list:
['red', 'white', 'blue']
•
A string:
'Check out Joan Osborne, super musician'
•
A tuple:
(800, 400, 310)
You can still get to the items (aka elements) in the collection, by indexing:
colors = ['red', 'white', 'blue']
•
colors[0]
has value 'red'
•
colors[1]
has value 'white'
•
colors[2]
has value 'blue'
Indexing starts at zero,
not at one
The len function
returns the length of
the sequence, that is,
the number of items in
the sequence.
Patterns for Looping (“iterating”) through the items of sequences
Beginning To End
for k in range(len(sequence)):
... sequence[k] ...
Basic idea: You want to iterate through ALL
of a sequence, from its beginning to its end.
In this example, the code iterates through the entire
FORWARDs looking at EVERY ITEM, printing those items.
for k in range(len(sequence)):
print(sequence[k])
Be sure that you understand the use of the index k in the above example. It is not a “magic”
symbol; it is just an ordinary variable that goes 0, 1, 2, ... per the range statement. Do you see now
why the range statement is defined to start at 0 and ends one short of the value of its argument?
Patterns for Looping (“iterating”) through the items of sequences
Other Ranges
Form 1
for k in range(NUMBER):
... sequence[BLAH] ...
where NUMBER is the number of items in the
sequence to examine and BLAH is some
formula involving k that is carefully crafted to
produce exactly the right indices.
Basic idea: You want to iterate through PART of a sequence, or
perhaps through it backwards, or some combination of both.
Form 2
for k in range(BLAH):
... sequence[k] ...
where BLAH is some range
OTHER than 0, 1, 2, ... that is
carefully crafted to produce
exactly the right indices.
Form 3
m = ...
for k in range(NUMBER):
... sequence[m] ...
where NUMBER is the number of items in the
sequence to examine and m is an auxiliary
variable that is carefully controlled to
produce exactly the right indices.
Examples for each form. In each example, the code iterates through the code BACKWARDS looking at EVERY 3rd ITEM, printing those items.
last = len(sequence) – 1
for k in range(len(sequence) // 3):
print(sequence[last - (k * 3)])
last = len(sequence) - 1
for k in range(last, -1, -3):
print(sequence[k])
last = len(sequence) – 1
m = last
for k in range(len(sequence) // 3):
print(sequence[m])
m = m - 3
Patterns for Looping (“iterating”) through the items of sequences
Selecting Items
Basic idea: You examine all or some of the items in the sequence, but
use an IF statement to process only ones that satisfy a certain condition.
for k in range(len(sequence)):
if ... sequence[k] ...:
... sequence[k] ...
I have shown the pattern combined
with the Beginning-To-End pattern,
but it could also be used with any of
the Other-Ranges patterns.
In this example, the code selects the items that are
integers (and counts them, returning the count).
def count_integers(sequence):
count = 0
for k in range(len(sequence)):
if type(sequence[k]) == int:
count = count + 1
return count
Patterns for Looping (“iterating”) through the items of sequences
Finding something
Basic idea: You examine all or some of the items in the sequence,
looking for one that satisfies a certain condition. When you find such
an item, you return its index (position) in the sequence. If there is no
item in the sequence that meets the condition, you return -1.
for k in range(len(sequence)):
if ... sequence[k] ...:
return k
return -1
I have shown the pattern combined
with the Beginning-To-End pattern,
but it could also be used with any of
the Other-Ranges patterns.
In a variation of this pattern, the function might return True if it
found an item that meets the condition, or False if not. Or, in
another variation, it might return the item that is found, or None if
there is no item in the sequence that meets the condition.
In this example, the code returns the index of the first
item it finds that is bigger than 20.
def find_bigger_than_20(sequence):
for k in range(len(sequence)):
if sequence[k] > 20:
return k
return -1
Note the placement of the second return statement.
This is NOT the time for an if-else in the loop!
Patterns for Looping (“iterating”) through the items of sequences
Two places at once
Basic idea: At each iteration of the loop, you examine
TWO items in the sequence during that iteration.
for k in range(BLAH):
... sequence[INDEX_1] ... sequence[INDEX_2] ...
where:
• BLAH is some range appropriate to the problem,
• INDEX_1 is some function of k that gives ONE
of the “two places at once” to examine, and
• INDEX_2 is another function of k that gives the
OTHER of the “two places at once” to examine.
Typically, INDEX_1 or INDEX_2 (but not both!) is
simply k.
You often have to restrict the range to less
than the entire sequence when using this
pattern, as in the example to the right.
In this example, the code compares each item to
the NEXT item in the sequence, counting the
number of times that the next one is bigger than
the current one, and returns that count.
For example, if the sequence is
[9, 7, 12, 35, 24, 25],
the function returns 3 because 12 is bigger than 7,
35 is bigger than 12, and 25 is bigger than 24.
def count_increases(sequence):
count = 0
for k in range(len(sequence) - 1):
if sequence[k + 1] > sequence[k]:
count = count + 1
return count
Patterns for Looping (“iterating”) through the items of sequences
Parallel sequences
Basic idea: At each iteration of the loop, you examine TWO
SEQUENCES, looking at the kth item of each, during that iteration.
for k in range(len(sequence1)):
... sequence1[k] ... sequence2[k] ...
The above assumes that the two
sequences are the same length, but
variations can remove that restriction.
Using parallel sequences is an error-prone approach,
since it is easy to modify one sequence and forget to
modify the “parallel” sequence. Nonetheless, this is an
important pattern to understand.
In this example, the code compares each item in one sequence to its
“parallel” item in the other sequence, counting the number of times
that the latter is bigger than the former, and returns that count.
For example, if the sequences are:
[11, 22, 10, 44, 33, 12]
[55, 10, 30, 29, 31, 30]
then this function returns 3,
since 55 > 11 and 30 > 10 and 30 > 12.
def count_bigger(sequence1, sequence2):
count = 0
for k in range(len(sequence1)):
if sequence2[k] > sequence1[k]:
count = count + 1
return count
Patterns for Looping (“iterating”) through the items of sequences
Max or Min
Basic idea: You examine all or some of the items in the sequence, looking for the biggest
(or smallest) one. Return the index of that item (or perhaps just the item itself).
k_for_max = 0
for k in range(1, len(sequence)):
if sequence[k] > sequence[k_for_max]:
k_for_max = k
return k_for_max
The above is for Max. Reverse the
comparison in the IF statement for Min.
In this example, the code returns the index
of the smallest item in the sequence.
def index_of_min(sequence):
k_for_min = 0
for k in range(1, len(sequence)):
if sequence[k] < sequence[k_for_min]:
k_for_min = k
return k_for_min
In a variation of this pattern,
the function might return
sequence[k_for_max]
instead of k_for_max.
I have shown the pattern combined
with the Beginning-To-End pattern,
but it could also be used with any
of the Other-Ranges patterns.
This pattern requires that the
sequence be non-empty.
Do you see why the range
starts at 1, not at 0?