This worksheet is designed to accompany Chapter 5 of Introduction to Scientific Programming: Computational Problem Solving Using Maple and C by Joseph L. Zachary. In it, we will use Maple to explore what happens when a function call is evaluated.
To use this worksheet you will need to use some extensions to Maple that we have created. Read in our function simulation package by evaluating the Maple command below. (You will need to have first installed our custom Maple library and configured Maple to use it.)
In this laboratory we will be exploring how user-defined functions are defined and what happens when they are called.
Recall that a function is defined by specifying its name, its formal parameters, and its body. For example, here is a function called "avg" that returns the average of its two parameters.
> avg := (x,y) -> (x + y) / 2;
Once we have defined a function in this way, we can use it as if it were built in. For example, we can compute the average of 6+6 and 3*6 via
> avg(6+6, 3*6);
Here, 6+6 and 3*6 are called the actual parameters, to distinguish them from the formal parameters that are used to define "avg".
Maple follows a three-step process when evaluating a call to a programmer-defined function. In the call
> avg(6+6, 3*6);
the expressions "6+6" and "3*6" are evaluated to obtain "12" and "18", respectively.
Maple's second step is to substitute the evaluated actual parameters for the corresponding formal parameters in the body of the function. In the case of the call to "avg" above, 12 corresponds to x and 18 corresponds to y. The body of "avg" is
> 1/2 * x + 1/2 * y;
When Maple substitutes 12 for x and 18 for y, it obtains
> 1/2 * 12 + 1/2 * 18;
Maple's third step is to evaluate the expression that results from the substitution just as if it had been typed directly into Maple. Since the result of the expression above is 15, this is also the result of the original function call.
> (12 + 18) / 2;
When you are first learning how to write functions, it can be difficult to visualize these three steps. We have created a special function called "functionTrace" to help you do this. Let's try it out.
Suppose you would like to see what really goes on when you make the function call "avg(6+6, 3*6)". You can do this by enclosing the function call in backquotes and passing it as a parameter to "functionTrace".
> functionTrace(`avg(6+6, 3*6)`);
Look at all of the information that is printed out. We are first shown the formal parameters and the body of "avg". We are then shown the results of evaluating the actual parameters, the expression obtained when the actual parameters are substituted for the formal parameters in the body of the function, and the result of evaluating this expression. Best of all, "functionTrace" will work for any programmer-defined function that you create.
For example, let's define a function that cubes its argument.
> cube := (x) -> x^3;
We can trace Maple's process of evaluating a call to "cube" as easily as we did a call of "avg".
We can use "functionTrace" to illustrate a number of mistakes that beginning programmers tend to make when calling functions.
When you call a function, it is important that there be the same number of actual parameters in the function call as there are formal parameters in the function definition. Otherwise, Maple will not be able to match each actual to a formal prior to doing the substitution. Here, for example, we give only one parameter to "avg":
This is a mistake, of course. Here we give three parameters to "avg":
This is also a mistake. If we type these function calls directly into Maple instead of into the simulator:
we see that Maple always complains if you provide too few parameters, but ignores the extra parameters if you supply too many. Nevertheless, you should always supply exactly the right number of parameters to a function.
For some reason, many beginning students appear to expect that the actual parameters will get to the function by magic. One commonly attempted tactic is to try to assign values to variables with the same name as the formal parameters and to then "call" the function by just mentioning its name.
> x := 12;
> y := 18;
Not surprisingly, this doesn't work! If we run this function "call" through the tracer
we discover the nature of our mistake.
Of course, it would work if we passed "x" and "y" as the two actual parameters
since they now have the appropriate values.
Of course, there is nothing special about the names "x" and "y". We could just as easily have used "first" and "second":
> first := 12;
> second := 18;
> functionTrace(`avg(first, second)`);
From the point of view of the tracer, the last two invocations of "avg" were identical.
Remember: it doesn't matter how you write the actual parameters-all that matters is what they evaluate to. And when you are using a function, it doesn't matter at all what the formal parameters are called-that is pertinent only within the implementation of the function.
Let's experiment with a different programmer-defined function now.
> distance := (t) -> 1/2 * g * t^2;
If we call distance
you will notice that the variable "g" appears in the result. This is because it appears in the body of the function definition but is NOT a parameter. As a result, it appears in the substituted body. When Maple evaluates the substituted body, it tries to look up the value of "g" like it would for any variable, and failing to find one it leaves "g" as it is.
This will be more evident if we trace the evaluation of the function call.
If we now give "g" a value
> g := 9.8;
and then evaluate the call to "distance" again
we get a numerical answer. Notice that the value of "g" is not looked up until the substituted body is actually evaluated. Thus, if we change "g"
> g := 15.5;
and try one more time
we get a different answer.
You should now experiment with defining some functions of your own. Implement the following functions. Test them out by calling them directly from Maple, and also use functionTrace to help you visualize what goes on when they are called. Remember: to use functionTrace, give it as a single parameter a function call enclosed in backquotes.