What is New Here?

Here we explain the new features in the code nr2.py.

The code starts with the definition of functions. These definitions are stored by Python and not executed until they are needed. The first executable statement is at the very end of the code, namely

main()

This is a function call. Control then passes to the function main() in the code above. When the main program needs to evaluate the functions f(x) and dfdx(x) control passes to them. Control passes back to the calling program when a function is finished.

We have used a full line of comment hashes ###### to mark off the function definitions that start with the key word def. This is not required, but it helps to find the components of the code.

We have defined “functions” for evaluating the function and its derivative, and we turned the “main” program into a function, as well. This last step is not required. It is a matter of style. You can also omit the def main():, if you like, and the last line main() and undo the indentation of the main code.

The subprogram for evaluating the function takes only three lines:

def f(x):
    """Evaluate the function"""
    return 4*x - math.cos(x)

The subprogram for the derivative is similarly short. We put their definitions at the top of the code, because Python requires that they be defined before they can be used.

The general pattern for the definition is

def function-name(function-argument-list):
   """Comment"""
   function-statements
The comment explaining the purpose of the function is actually optional, but I recommend you include it. Standard Python style puts the comment line immediately after the def line. (With three quotes you may break a long string into multiple lines.)

The result of the subprogram f is called its “return value”. The return statement in a function definition has the general form

  return expression
It causes the expression to be evaluated and the result handed back as the return value of the function. You could also do it in two steps with an intermediate variable:

def f(x):
    """Evaluate the function"""
    y = 4*x - math.cos(x)
    return y

With these definitions, the functions can be used in a natural way in the code below:

    pnew = p - f(p)/dfdx(p)

Logically, in order to assign a new value to pnew the Python interpreter realizes that to evaluate the expression f(p)/dfdx(p) in the main program, it has to hand over the current value of p to the subprogram f, which determines the value of the function for that value of x and hands the result back to the main program. The compiler does the same for the derivative. Finally, it divides and subtracts the result from p. We say that the main program “calls” the function subprograms. Each subprogram does its work and and then returns control to the calling program.

The function argument list (sometimes called the “parameter list”) specifies input and/or additional output values for the function. It declares their types as well. In this case we have only one input value, which must be of numerical type, and the output value is returned as the value of the function. With more parameters, they are separated by commas. Here is what it looks like with two:

def f(x, c):
    """Evaluate the function"""
   return c*x - math.cos(x)
}
Of course, in that case we would have to call the function with
  f(x,4)
to get the same result as before.

We say that when the function is called, the input parameters are “passed” to the function. For the input parameters we can think of this operation as an implied assignment. In this example we have, in effect,

   x = p
   c = 4
In the next lesson we make more precise sense of such implied assignments when we discuss the concept of the “scope” of a variable's definition. Note that in these implied assignments above, that the names on the left-hand side (lhs) belong to the subprogram and the names on the right-hand side (rhs) belong to the calling program.

The names of the subprogram arguments may differ from the names in the calling program, but the purpose and data type must match in the order given. Of course the compiler can handle assignments that convert among the base numeric types float and int.

The implied assignment can involve expressions. In the quadratic.py example we could have written math.sqrt(b*b - 4*a*c). The compiler evaluates the expression and passes the result to math.sqrt.

We will discuss function subprograms in greater depth in the next lesson.