6.6. Functions can Call Other Functions¶
It is important to understand that each of the functions we write can be used and called from other functions we write. This is one of the most important ways that computer scientists take a large problem and break it down into a group of smaller problems. This process of breaking a problem into smaller subproblems is called functional decomposition.
Here’s a simple example of functional decomposition using two functions. The
first function called square
simply computes the square of a given number.
The second function called sum_of_squares
makes use of square to compute
the sum of three numbers that have been squared.
Even though this is a pretty simple idea, in practice this example
illustrates many very important Python concepts, including local
variables along with parameter passing. Note that when you step
through this example, codelens bolds line 1 and line 6 as the
functions are defined. The body of square is not executed until it is
called from the sum_of_squares
function for the first time on
line 11. Also notice that when square
is called there are two
groups of local variables, one for square
and one for
sum_of_squares
. As you step through you will notice that x
,
and y
are local variables in both functions and may even have
different values. This illustrates that even though they are named
the same, they are in fact, very different.
Now we will look at another example that uses two functions. This example illustrates an important computer science problem solving technique called generalization. Assume we want to write a function to draw a square. The generalization step is to realize that a square is just a special kind of rectangle.
To draw a rectangle we need to be able to call a function with different arguments for width and height. Unlike the case of the square, we cannot repeat the same thing 4 times, because the four sides are not equal. However, it is the case that drawing the bottom and right sides are the same sequence as drawing the top and left sides. So we eventually come up with this rather nice code that can draw a rectangle.
def draw_rectangle(width: float, height: float) -> None:
for i in range(0, 2, 1):
turtle.forward(width)
turtle.left(90.0)
turtle.forward(height)
turtle.left(90.0)
return None
The program doesn’t “understand” that you’re drawing a rectangle or that the parameters represent the width and the height. Concepts like rectangle, width, and height are meaningful for humans. They are not concepts that the program or the computer understands.
Thinking like a computer scientist involves looking for patterns and relationships. In the code above, we’ve done that to some extent. We did not just draw four sides. Instead, we spotted that we could draw the rectangle as two halves and used a loop to repeat that pattern twice.
But now we might spot that a square is a special kind of rectangle. A square simply uses the same value for both the height and the width. We already have a function that draws a rectangle, so we can use that to draw our square.
def draw_square(size: float) -> None: # a new version of drawSquare
drawRectangle(size, size)
return None
Here is the entire example with the necessary set up code.
There are some points worth noting here:
Functions can call other functions.
Rewriting draw_square like this captures the relationship that we’ve spotted.
A caller of this function might say draw_square(50.0). The parameter of this function,
size
, is assigned the value of the float 50.0.In the body of the function,
size
is just like any other variable.When the call is made to
draw_rectangle
, the values the variablesize
is fetched first, then the call happens. So as we enter the top of the function draw_rectangle, its variableswidth
andheight
in that function are both given the value 50.0.
So far, it may not be clear why it is worth the trouble to create all of these new functions. Actually, there are a lot of reasons, but this example demonstrates two:
Creating a new function gives you an opportunity to name a group of statements. Functions can simplify a program by hiding a complex computation behind a single command. The function (including its name) can capture your mental chunking, or abstraction, of the problem.
Creating a new function can make a program smaller by eliminating repetitive code.
Sometimes you can write functions that allow you to solve a specific problem using a more general solution.