Additional Control Structures
5.1 While Loops
5.1.1 Infinite Loops
When working with a for loop, the overall number of iterations is naturally bounded based on the length of the original sequence. When working with a while loop, the overall number of iterations is not explicitly bounded. It is determined by a combination of the particular loop condition and the changing state of the underlying values. This introduces a potential pitfall with serious consequences, namely the possibility that the while loop never ends.
We commonly call such a situation an infinite loop. Here is a blatant example, while True:
print'Hello'
Clearly this loop condition will never fail, and so the loop continues forever. As a side note, this may be a good time to learn how to manually force the interpreter to stop its execution; this is typically done by entering the control-c keystroke.
Although we may be wise enough to avoid such a silly example, many reasonable-looking efforts may mistakenly lead to infinite loops. For example our loop to play a game multiple times as written on page 160 might technically lead to an infinite loop if the user is addicted to the game. As a programmer, we have no guarantee that the user will ever turn down the offer for another game. Of course, we do not consider such design an error in the judgment of the programmer (we might eventually raise questions as to the judgment of the player). Other infinite loops are clearly the fault of the programmer, not the user.
Looking back at our index-based while loop on page 162, we see that we had to explicitly increment the value ofias part of the loop body. This was a step that was implicit in the equivalent for loop construct. If we were to forget that important step, we end up with the following infinite loop (presuming that the sequence has one or more entries and that the first entry does not match the target value):
i = 0 # index−based search
found = False # pessimism
while i < len(data) and not found:
if data[i] == val:
found = True # we found it
Sinceiis never incremented, the conditioni < len(data)remains true, and since we contin-ually comparedata[0]tovalwe never find it.
In general, determining whether a while loop repeats infinitely requires a deeper understanding of the logic. For example, our code for Euclid’s algorithm on page 161 has a loop conditionwhile v != 0. It is not immediately clear that this loop will ever end, although it turns out that it always does (see For the Guru on page 10). But it is the responsibility of the programmer to ensure the appropriate termination.
5.2 Functions
Functions can serve as the ultimate control structure. They allow a series of complicated instructions to be encapsulated and then subsequently used as a single high-level operation.
We are already familiar with the use of functions from the perspective of a caller. When a function is called, control is passed behind the scene to instructions that accomplish some task. Parameters might be sent by the caller to affect the behavior. The function may return some final information to the caller when the action is completed.
There exists a built-inmaxfunction, but for strings it returns the “maximum” relative to the alphabetical ordering of those strings. In many contexts, we are interested in the maximum-length string. For example, a healthy eater may scan a list of ingredients with a suspicious eye on the longest entry (e.g., monosodium glutamate). A person preparing name tags for a party may be interested in the length of the longest name from a guest list.
As a concrete example, we will design and implement a function that locates the maximum-length string from a sequence of strings. In this section, we are ready to take a look backstage, bridging the gap between the perspectives of the caller and the callee.
Usage
The key to creating a new function is to first consider how it will be used. What will the function’s name be and what high-level task will it accomplish? What information will a caller need to send and what information should the function return?
For the case of determining a maximum-length string, we choose to name our func-tionmaxLength(this differentiates it from the existingmaxfunction). We expect the user to send us a sequence of strings as a parameter. Our function will locate the string that has the longest length and return that string to the caller. With these decisions in place, we can already envision code that might be used by a caller. For example in the context of analyzing foods, the following syntax can be used:
ingredients = ['carbonated water','caramel color',
'phosphoric acid','sodium saccharin','potassium benzoate', 'natural flavors','citric acid','caffeine',
'potassium citrate','aspartame','dimethylpolysiloxane'] concern = maxLength(ingredients) # calling our function
print concern
Notice that the calling syntax ismaxLength(ingredients). Our function is not an official method of thelistclass, so it cannot be invoked using the syntaxingredients.maxLength( ). For a function to be an official method of a class, it must be defined as part of the original class definition (this will be the focus of the next chapter).
Implementation
A straightforward algorithm to find the maximum-length string is to scan the entire list, keeping track as we go of the longest entry that we have seen. In case of a tie, our function will return the first of the equally long strings it encounters. Our function definition is written as follows:
1 def maxLength(stringSeq):
2 longSoFar ='' # empty string, by default
3 for entry in stringSeq:
4 if len(entry) > len(longSoFar): # even longer 5 longSoFar = entry
6 return longSoFar
A function declaration always begins with the keyworddef(short for define). It is followed by the name of the function (in this casemaxLength). Next, parentheses enclose a series of parameters that are to be sent by the caller (in this case,stringSeq). If a function does not require any parameters, there still must be opening and closing parentheses to demarcate the lack of parameters. Finally, an indented body contains the code that is executed when the function is invoked. Let’s analyze the components of this example more carefully.
ingredients stringSeq list
...
FIGURE 5.2: The parameter is identified as ingredients from the context of the caller, yet as stringSeq within the context of the function body.
Parameters
The namestringSeqwe chose for the parameter is up to our discretion. This is known as a formal parameter; it serves as a placeholder for that piece of information indicated by the caller, known as the actual parameter. We cannot assume to know the variable name used by the caller. One caller may use the syntaxmaxLength(ingredients), while anothermaxLength(guests). To provide guidance, we choose a formal parameter name that suggests its meaning (although there is no guarantee that this is what the caller sends;
more on this in Section 5.5). Each time our function is called, the system assigns the identifier we chose as a formal parameter to the actual parameter indicated by the caller, as shown in Figure 5.2. The code in the body of the function uses the formal parameter to identify the underlying piece of information (in this example, at line 3).
Body
The body of our function, shown in lines 2–6, implements our scanning algorithm. The identifierlongSoFaris introduced at line 2 to track the longest string we have seen. Initially, we have not seen any strings, so we let the empty string be the longest thus far. The for loop of lines 3–5 checks each entry of the caller’s sequence, looking for a string longer than any thus far. When the loop completes,longSoFarwill identify the overall longest string in the original sequence.
However, the identifierlongSoFar has what we term local scope. That identifier cannot be directly accessed by any block of code other than the body of the function; it is created solely for our use in processing the current function call. This is a good thing because it means that we do not need to worry about what variable names are used by the caller, and vice versa. If we declare a variablexand the caller also has a variable namedx, these do not interfere with each other.
However, this local scope means that the caller cannot directly access the final result using the variablelongSoFar. Instead, we rely on an explicit use of areturnstatement to communicate this information back to the caller.
Return value
At line 6 of our function body, we use the commandreturn longSoFar. This indicates the value to be communicated back to the caller. It is not our concern what the caller plans to do with this information; we simply have a responsibility to inform the caller based upon the desired semantics of our function. Let’s assume that the caller originally invoked our function with a command, such asconcern = maxLength(ingredients). From the caller’s perspective, the identifier concernis being assigned to the return value of the function.
str
'dimethylpolysiloxane'
concern longSoFar
FIGURE 5.3: The mechanism for passing a return value is similar to that for a parameter.
Upon return, the caller’s identifier concern is associated with the object identified internally as longSoFar.
The internal mechanism for communicating an underlying object is quite similar to that of parameter passing. The caller’s identifier is simply associated with the indicated return value, as shown in Figure 5.3. Although the local identifierlongSoFarwill cease to exist as the function ends, the underlying object will be available to the caller.
Thereturnstatement happened to be the last line of the body in our example, yet this is not always the case. If areturncommand is executed elsewhere within the body (for example, within a conditional), the execution of the function immediately ends with any specified value passed back to the caller. It is also possible to use areturnstatement without any subsequent value. Such a syntax is used to end the execution of the function without returning any formal value to the caller. Without an explicit object returned the special valueNonewill be returned. In fact, if the execution of a function reaches the end of the body without an explicitreturnstatement,Noneis automatically returned.