//
you're reading...
Uncategorized

Patterns and Anti-patterns in Python For Loops

In the last lecture we learned that Python has objects that come from  4 basic iterator types, which are the string, list, tuple, and dict types, and they can all be used in for loops.

In this lecture we look at commonly used working for-loop patterns over those object types. We also consider some problem for-loop anti-patterns, which should be avoided. Again, these patterns work for any object from the fantastic-four iterator types. Now let us call one such object items.

The counting pattern

The counting pattern is used to count the number of items. Note that the built-in function len already does. But is is useful to see how to count each single item i in our iterable items.

count = 0
for i in items:
    count = count + 1
print "There are", count, "items."

The filtered-count pattern

A variation on the counting pattern involves filtering. When filtering, we use an if statement to decide whether we should count an item or not. Suppose we wish to count the number of times an item z is in the set of items:

z-count = 0
for i in items:
    if (i == z):
        z-count = z-count + 1
print "There are", z-count, "items equal to z."

Of course this is a pattern, and we could replace the if test (i==z) with any predicate test – that is code that returns True or False.


The reduce or accumulate pattern

Suppose it makes sense that the items can be added added all together. That process is called reduction or accumulation and has a simple pattern:

total = 0  # start an accumulation at the identity
for i in items:
    total = total + i

print "The total is", total

For this pattern, we could replace the summation (+) with any
binary operation; for example, multiplication (*), the min or the max function.

The filtered-reduction pattern
Let us simply compose the two patterns:

total = 0
for i in items:
    if (i == z)
       z-total = z-total + i

print "The z-total in items is", z-total

The extreme pattern

Suppose we wish to find the most extreme value of an item in the set, usually the largest or smallest value. Here is an approach in which we use a variable largest (or largest-so-far) that assumes the first item is the largest and then corrects that assumption with each iteration.

largest = items[0]
for i in items:
    if (i > largest):
        largest = i
print "The largest item is", largest

The extreme-index pattern

We often wish to determine a pointer (or index) of the most extreme value in a set of items, rather than the actual extreme value itself. In lists, tuples, strings (and later arrays) this pointer is simply a number between 0 and the length of set of items. As above, we will assume the first item (index zero) holds the extreme value, and iterate to correct that assumption:

l-index = 0
largest = items[0]
for idx in range(len(items):
    if (items[idx] > largest):
        largest = items[idx]
        l-index = idx

print "The index of the largest item is", l-index

The filter or extract pattern

A special case of a filtered-accumulation is called simply a filter. Instead of summing the filtered items (for example), we collect the filtered items into a new list. The new list is said to be a filter or an extract of the original. Here we assume we have previously written a predicate test function.

new = [] # creates a new empty list
for i in items:
    if test(i):
       new.append(i)

The mutated loop Anti-pattern
One common error is to modify or mutate the items in the middle of the loop. For example.

for i in items:
    if test(i):
       items.remove(i)

This can produce highly unexpected results and should be avoided.

Advertisement

Discussion

No comments yet.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

%d bloggers like this: