IMSE Chapter 9

IMSE-09(Programming).nb
Programming in Mathematica
RCH 11/18/01
revised 6/25/02 - rjerwin
small correction 9/1/04 - wl

This notebook explores some of Mathematica's programming capabilities.  If you haven't programmed much or at all, don't worry - this is as good a time as any to start learning!

In  previous IMSE chapters, we have seen  some of the many commands built into Mathematica.  In many situations in science and engineering computations, however, it may be necessary to execute these commands in a certain order with the choice of commands dependent on the output of previous commands.  This is where programming becomes important.  Mathematica has built in many standard programming features.  In addition, you can use programs developed in other languages such as C++ and Fortran as part of a Mathematica notebook.  In this section we will focus on the use of Mathematica's programming tools.  You can find out how to use programs in other languages by reading the MathLink section of Help.

Repetitive (Recursive) Operations

"Loops" evaluate an expression (such as a Mathematica command or an equation) repeatedly.  In this section, we will explore various ways of setting up repetitive operations in Mathematica.  We will look at "Do" loops, "While" loops, and "For" loops.  These are quite similar to commands in Basic, Fortran, and C++.

Do Loops

A Do loop repeats a process a certain number of times, changing an index (call it k) each time.  After the specified number of repetitions, the loop stops.  Here is an example that computes the cube of a set of numbers.

Do[fcube[k] = k^3, {k, 0, 20}]

Let's look at the setup of the Do loop.  The outer square brackets contain the steps to be carried out along with the specifications of the loop index k.  In this example, we assign the cube of the index to a function fcube[k].  The curly brackets contain a list consisting of the loop index, its starting value (0) and its ending value (20).  Mathematica assumes that we want the index incremented by 1 unless we specify otherwise.  To see what we get, we can ask for the value of the function

fcube[2]

Or, we can use another Do loop to print the numeric values.

Do[Print[fcube[k]], {k, 0, 20}]

Suppose we want only every other value.  We can change the increment for the loop index as illustrated below by adding to the loop index list.

Do[Print[fcube[k]], {k, 0, 20, 2}]

You may have realized that we could have carried out the previous calculations using the Table command.  The advantage of the Do loop is that we can change the procedure within the loop based on the results of the calculation.  We'll explore that feature more closely below.

While Loops

The While loop is somewhat more flexible than the Do loop because we have more control over the finishing condition.  The While loop includes a test statement: during each run-through of the loop the criterion specified in the statement is tested.  If it is satisfied (True) the loop is executed.  If the criterion is not satisfied (False), the program exits the loop.  The following cell performs the same calculation as that in our original Do loop above.

j = 0;
While[j < 21, g[j] = j^3; j++]

Note that we have to set an initial value for j outside the loop.  The first statement inside the While bracket is the test statement: as soon as j reaches 21 (so that the test condition j < 21 is false) the loop ends.  The test statement is separated from the rest of the While loop, the"body," by a comma.  The second statement (g[j] = j^3) is our computation.  The third statement, separated from the second by a semicolon, increments j by 1 at each run-through of the loop.  (This syntax is the same as that used in C++.)  If we had other body statements, we would use semicolons to separate them.)

Let's see what we get from the computation.

g[15]
g[21]

The last result indicates that Mathematica did not evaluate (21^3) because the While loop exited once we got to j = 21.

Here is an example of a While loop that makes a somewhat different kind of test.

y = 0;
j = 0;
While[y < 0.4, 
      y = Sin[0.1*j*π]; j++]
j

The largest integer value of j such that Sin[0.1*j*π] < 0.4 is 3.

CAUTION:  It is easy to make While loops that go on forever (so-called infinite loops).  If your program gets stuck in an infinite loop, you can use the menu bar tab "Kernal"->"Abort Evaluation" (or simply use Alt+.) to stop the execution of the program.  Here is an example to try.

j = 4;
While[j > 3, k = j; j++]

As a programmer, you need to check carefully the consistency of your loops.  Inserting Print statements into a loop is often a good way of debugging problem programs.

For Loops

More flexible than either the Do or While loops is the For loop.  Like the While loop, the For loop usually has a loop index, and we specify a test statement and an increment amount for the index.  However, for the For loop we can specify a starting value inside the loop and have more control over the repetitive operations.  The body of the loop contains the commands to be executed.

For[i = 0, i < 10, i++; Print[i]]

Now we have a loop index i that starts at 0 and stops when i ≥ 10.  The loop index is incremented and then printed, so the first printed value is 1 and the last is 10.

Here is another variation of the For loop:

total = 0;
For[j = 0, total <  100,  j++; 
      total = total + j^2]
Print["j = ", j, ", total = ", total]

This program evaluates the sum of the squares of the integers 1 +  (2^2)+ (3^2)+ (4^2)+ . . .  until the sum exceeds 100, at which point the loop exits.  The last line prints the final values for j and the total.

Branching Tests

The most powerful feature of programming is the ability to choose different procedures depending on the outcome of a test that itself may depend on the results of previous computations.  The "If" command is the most commonly used example of this type of conditional test.  An If statement contains the following ingredients:

1. a test;
2. the command(s) that should be executed if the test is TRUE;
3. (optional) the command(s) that should be executed if the test is FALSE.

The last part is sometimes called the "else" command(s), completing "If, Then, Else" sequence.  If the else commands are not provided and the test yields FALSE, the program moves on to the statement following the If statement.  Here is an example of an If command that prints the smaller of two numbers.  Try it out with various values for a and b.  What happens if a = b?

a = 1;
b = 3;
If[a < b,  Print[a],  Print[b]]

Modules

As your programs grow in complexity it may be helpful to split them up and organize them into "Modules."  In Mathematica a module is a set of commands that work together to produce a particular output.  The advantage of using modules is that you can incorporate complex series of commands within the module, and you can define variables that are local to the module and do not affect other variables with the same name outside the module.

Here we give an example that defines a function h[x,y] in terms of commands within a module.  We first set two variables u and z outside the module to show that they are unaffected by local variables inside the module.  Recall that the x_ and y_ subscripts tell Mathematica this is a function definition.  In addition, we use the delayed evaluation symbols := so that the function is not evaluated until called by other commands.  This procedure allows us to pass values (or symbols) to the Module.

u = 25.3;
z  =  17.1;

    
h[x_, y_] := 
      Module[{u, z}, 
u = 0; 
Do[
          u = u + k, {k, 1, x}]; 
z = 
          0; 
Do[
          z = z + j, {j, 1, y}]; 
Return[
          u*z]]

    
Print["h[2,3] = ", h[2, 3]]
Print["u = ", u]
Print["z = ", z]

h[2.4, 1.3]

Within the module, we have two local variables u and z.  These local variables are not accessible from outside the module, nor do they affect any previous definitions of u and z.  They are specified inside curly brackets as the first statement within the module.

The other commands in the module calculate the sum of integers from 1 through the integer x and 1 through the integer y.  The result is then multiplied together and the answer is returned as the value of the function h[x,y].  The last three commands print the values of h[2,3], u, and z.  The u and z values are just the values defined outside the module, illustrating that changing u and z within the module does not affect the values outside.  The local variables are said to have "local scope," meaning that they exist only within the module.

Exercise:  What happens if you ask for h[x,y] and x and y are not integers?

Mathematica Modules are much like subroutines in other computer languages.  By defining a module as a function, as we did in the previous example, you can pass variables or symbols to the module and return a numerical value.

Here is another Module example.  In this case we use the module to define an array that is used in statements outside the Module.

(*A module to fill in an array.*) 
Module[{}, 
Do[
        q[k] = 1/k, {k, 1, 
          5}]] (*End of module.*)

Note that we have not used any local variables, but the curly brackets must be included anyway.  When you activate the cell containing the module, nothing appears to happen, but Mathematica has in fact set up the array q[k] = (1/k).  The following cell prints the value of q[3].

Print["q[3] = ", q[3]]

Below is a similar module set up as a function.  In this case, the length of the array is passed to the module when the function f[] is called, so the module can be used to define arrays of varying length.

(*Another module to fill in an array.*) 
f[n_] := 
      Module[{p}, 
Do[c[p] = p^2, {p, 1, n}]; 
        Return[Table[c[p], {p, 1, n}]]]

f[8]

c[8]

Of course, these examples are rather contrived, and the results could have been generated much more easily with a single Mathematica command such as Array or Table.  The benefits of modules become more apparent the more complicated your program is.

All of the examples given here have been of fairly conventional procedural programming methods.  Mathematica can handle more sophisticated programming techniques such as functional programming, rule-based programming, object-oriented programming and string-based programming.  For further information on using Mathematica for programming, see "Getting Started/Demos" in Mathematica Help.