Start here

This is a very good introduction to the basic ideas of writing simple programs. Read it.

What language?

For many of the projects, you can program in whatever language you like. For some, I will require you to use Mathmatica. Therefore, if you do not yet have a language of choice, it might be easiest to learn your basic programming skills in Mathematica.

Another option is perl, which seems to have by far the best collection of basic online tutorials (here is a good one, for example) and also the best introductory-level book: Learning Perl, by Randal Schwartz and either Tom Christiansen or Tom Phoenix, depending on which version you get. Our library has two copies of the book, plus an online version. In that same section of the library, you can find books on several other languages. It's possible that the collection of resources for perl only seems best to me because perl is the language I'm most familiar with. Spend some time leafing through some books and/or reading through tutorials on other languages and see if you agree with me.

Or just stick with Mathematica. Just for definiteness, I will use Mathematica syntax for the remainder of this document, but the concepts are going to be basic enough that everything we do here should be easy to translate to another language.

Necessary skills


Assign values to, and perform mathematical operations on, variables

As in a math class, a variable is simply the name given to an empty box that holds a value that will probably change. When you say
x = 20;
you're telling Mathematica "until I tell you otherwise, whenever I say x, I mean 20."

Predict what this will do, and then enter it into Mathematica and see if you were right:

x = 3;
y = 7;
z = x + y;
Print[z];
Do the same with this bit of code:
x = 3;
x = 20 + x;
Print[x];
Mathematica always evaluates the right-hand side first, then puts the result into the variable named on the left-hand side. So even some expressions that are never true in the mathematical sense, like
x = x + 1;
make perfectly good sense as part of a program. The above says, "add one to x, and then set x to be the result of that computation."

Mathematica will not like this:

3 = x;
Why not?

Now a couple bits of shorthand that are so common that you must understand them.

This means "muliply x by 7"

x *= 7 
This means "add y to x (and call the resulting thing x)."
x += y 
Get the idea? In general
var [operation]= [something]
means the same thing as:
var = var [operation][something]
You don't ever have to use this shortcut, but you do have to understand it, because when you read other peoples' code, they will likely use it.

Finally, there is a frequently-used shortcut to the shortcut:

x ++;
means x = x + 1. Likewise,
x --;
means x = x - 1. Again, you could write perfectly good programs without using this particular shorthand notation. But people will look at you funny if you do.

One final thing about variables: it is usually wise to give your variables names that mean something. If your variable represents the height of a person, call it Height. If it represents the price of tea in China, call it PriceOfTea, or some such. I will be intentionally breaking that rule in this tutorial for reasons I'll be happy to explain to you if you're interested, but it's good programming practice to give your variables descriptive names. Do it.


Understand conditionals

In Mathematica, the construct is
If[ condition , 
        do this if condition is true , 
        do this if condition is false 
];
Note the capital I, the square brackets, and the placement of the commas.

Predict the output of each of the following blocks of code, and then see if you were right:

x = 7;
If[x > 8,
      x *= 4,
      x = x^2
];
Print[x];
x = 12;
If[x > 8,
      x *= 4,
      x = x^2
];
Print[x];
Very important note: x = 20 means "set x to 20," while x == 20 means "Is x equal to 20?" If you are writing an If, you almost definitely want the double-equals. Try this:
x = 9;
If[x = 8,
      Print["yes"],
      Print["no"]
];
Print[x];
If you want to do lots of things instead of just one thing, use semi-colons to end each individual command. Something like this:
If[ condition ,
        do this if condition is true;
        also do this;
        do this too; ,
        do this if condition is false;
        also this;
];
Sometimes, you'll want to do nothing if the condition is false. If you omit the second comma, that's what Mathematica assumes. For example, nothing at all will happen if you run this code:
x = 3;
If[x > 4, 
     Print["Something is way wrong!"];
];
If you want to test several conditionals, use Mathematica's Which:
Which[ condition 1 , 
       do this if condition 1 is true , 
       condition 2 , 
       do this if condition 2 is true , 
       condition 3 , 
       do this if condition 3 is true , 
       ...
];

Understand For loops

This should give you the idea:
For[i=1, i<20, i++,
      Print[i];
]
This means: Predict the result of this, then check your guess:
For[i=0, i<=100, i++,
      If[i < 50,
           Print[i, " is less than 50"],
           Print[i, " is at least 50"],
      ]
]

Understand While loops

Here is an example. Predict the output and then check your guess.
x = 0;
While[x<10 , 
   Print[x];
   x += 3;
];
The code between the comma to the closing bracket continues to execute over and over again as long as the condition before the comma is true.

It is very easy to accidentally create infinite loops with While. What would happen if you typed x -= 3 instead of x += 3 above?

What will this code do? Try it.

n = 1;
sum = 0;
While[sum<5,
  sum += 1/n;
  Print["The sum of the first ", n, " terms of the harmonic series is ", sum];
  n ++;
];

Understand arrays

An array can be thought of as a list of variables. (Please note that, if you ever become serious about computer science, you will have to understand that there is big difference between a list and an array. I'm using the word "list" in the conversational sense here, not in the official CS sense.)

Suppose you have three entrants in a watermelon-seed-spitting contest. For each of them, you need a variable to represent their spitting distance. So you do this:

Contestant1Spit = 13
Contestant2Spit = 14
Contestant3Spit = 11.4
That works. But what if one or two or several hundred additional contestants decide to enter your contest. If you've got 200 contestants, you really do need 200 variables. But they're all the same sort of thing. Whatever calculations you make on one of them, you're probably going to make on the others, too. So you group them all together in an array, like so (let's go back to just three contestants):
ContestantSpits = {13, 14, 11.4};
Now type:
ContestantSpits[[1]]
ContestantSpits[[2]]
ContestantSpits[[3]]
Do you see what's going on? ContestantSpits is an array that holds three variables, but each of the elements of the array can be accessed individually if need be. Here is a bit of code that finds the winner of the contest.
ContestantSpits = {13, 14, 11.4};
x = 0;
c = 0;
For[i=1, i<=3, i++,
     If[ ContestantSpits[[i]] > x , 
           x = ContestantSpits[[i]];
           c = i;
     ];
];
Print["The winner is contestant number " , c , ", with a spit of ", x , " feet!"];
Now suppose that, instead of getting only one attempt, each of the three contestants in your contest is allowed four attempts. We can store all the necessary information as a two-dimensional array, which is like a list of lists.
ContestantSpits = {{13,12,14,17},{11,9,13,8},{6,21,8,14}};

If you want to know the distance of the 3rd spit of the 2nd contestant, you'd type:

ContestantSpits[[2, 3]]
This will get you all the attempts of the 3rd contestant:
ContestantSpits[[3]]
Being mathematicians, you might be tempted to visualize this as a 3-by-4 matrix. That's not a bad idea if you're programming in Mathematica, because it allows you to capitalize on Mathematica's built-in matrix functions. For example, you could get the first attempts of all three contestants with:
Transpose[ContestantSpits][[1]]
or
ContestantSpits[[All, 1]]

Use functions/modules/subroutines

Let's walk through an example.

Suppose you have to write a program that does the following:

Here is a program that does it (for the number 6, let's say):
StartNumber = 6;
counter = 0;
number = StartNumber;
While[number < 100,
     number = number^1.1;
     counter ++;
];
Print["It took ", counter, " iterations to exceed 100."];
Now suppose you want to do this procedure for each integer between 2 and 50.

One option would be to write a For loop with all that stuff above inside it. Another (better) option is to modularize your program. Think of it like this:

For[i=2, i<= 50, i++,
     do the calculation for the integer i;
     print the result;
];
That's the big picture. Now you write a subprogram (Mathematica calls it a module) to do the calculation. Let's give our module a descriptive name: ExponentiateUntil100. Here it is:
ExponentiateUntil100[i_] := Module[
   {counter, number},
   counter = 0;
   number = i;
   While[number < 100,
       number = number^1.1;
       counter ++;
   ];
   Return[counter];
];
We'll talk about what all that means in a second, but for now, understand that we have created what a mathematician (like me) would think of as a function. That is, it's a machine that takes an input (i) and returns an output. Test it out by typing:
ExponentiateUntil100[3]
ExponentiateUntil100[6]
ExponentiateUntil100[7]
Now we can rewrite our original program like this:
For[i=2, i<=50, i++,
     result = ExponentiateUntil100[i];
     Print["The result for ", i, " is ", result];
];
This might seem like an awful lot of trouble. But as your programs get bigger and more complicated, it is essential that you be able modularize your thought process by modularizing your programs. Keep each individual task "in a box." Now let's talk about some of the specifics of the module.

The syntax is

ModuleName[InputVar1_, InputVar2_, ...] := Module[
        {declare local variables here} , 
        Do stuff ;
        Do more stuff ;
        Do still more stuff ;
        etc. ;
        Return[something];
];

A module might have zero, one, two, three, or more input variables. Our module above had one input variable. We called it i, but we could have called it (almost) anything we wanted. Don't forget to follow your variable names with underscores.

The local variables are variables that are created only for the purposes of doing temporary calculations within the module. They can't be accessed from outside the module and will be forgotten about once the module is finished doing its thing. The input variables are automatically considered local variables. You need not include them here.

Again, as a mathematician, you might think of these modules like mathematical functions: i.e. "input-output machines." The output is what goes inside the Return command. Your module might return a number, a word, an array, whatever.


Exercises

  1. Write a program that prints the squares of the first 100 positive integers.
  2. Write a module named Square that takes one input variable and returns its square. Then rewrite your program from #1 to include this module.
  3. Read about Mathematica's random-number-generating commands. You'll need them for most of the exercises below.
  4. Write a program that flips a fair coin 100 times and outputs the results, like this:
    tails
    tails
    heads
    tails
    heads
    heads
    .
    .
    .
    
  5. Write a module called FlipNTimes that flips a fair coin n times and returns the number of heads that were flipped.
  6. Now consider a coin that comes up heads with probability p (and tails with probability 1-p). Write a module that takes two inputs (p and n), flips the probability p coin n times, and returns the number of heads.
  7. Similar to #4, roll a fair six-sided die 100 times and output the results.
  8. Roll a fair six-sided die 1000 times and output a table of how often each number was rolled. E.g.
    1 was rolled 166 times
    2 was rolled 150 times
    3 was rolled 170 times
    4 was rolled 173 times
    5 was rolled 165 times
    6 was rolled 176 times
    
  9. Simultaneously roll a fair orange six-sided die and a fair black six-sided die. Do this 1000 times, and report the number of times each orange/black combination appeared. E.g.
    Orange 1 / Black 1 was rolled 31 times
    Orange 1 / Black 2 was rolled 33 times
    Orange 1 / Black 3 was rolled 35 times
    .
    .
    .
    Orange 6 / Black 6 was rolled 25 times
    
    HINT: you'll want to start with a 6-by-6 array of zeroes. See Mathematica's Table command.
  10. Write a module that rolls a fair six-sided die repeatedly until a six appears, and returns the number of rolls required.
  11. Use your module from the previous problem to do the following: roll a fair six-sided die until a six appears, repeat that process 1000 times, and report how many times each number of rolls were required. E.g.
    On 166 occasions, the first six appeared on roll number 1
    On 136 occasions, the first six appeared on roll number 2
    On 117 occasions, the first six appeared on roll number 3
    On 95 occasions, the first six appeared on roll number 4
    On 79 occasions, the first six appeared on roll number 5
    .
    .
    .
    
  12. Have 10 people flip a fair coin 100 times each. Find the person who flipped the most heads, and record how many heads he or she flipped. Now repeat that game 1000 times and report how many times the winner flipped a given number of heads. E.g.
    .
    .
    .
    On 2 occasions, the winner flipped 50 heads
    On 2 occasions, the winner flipped 51 heads
    On 23 occasions, the winner flipped 52 heads
    On 28 occasions, the winner flipped 53 heads
    On 69 occasions, the winner flipped 54 heads
    On 100 occasions, the winner flipped 55 heads
    On 121 occasions, the winner flipped 56 heads
    On 157 occasions, the winner flipped 57 heads
    On 118 occasions, the winner flipped 58 heads
    On 124 occasions, the winner flipped 59 heads
    On 78 occasions, the winner flipped 60 heads
    On 65 occasions, the winner flipped 61 heads
    .
    .
    .