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:
- invent a new variable called i and set it to 1
- Do what's in the loop (in this case, print i)
- At the end of the loop, add one to i
- Keep doing it as long as i is less than 20
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:
- Take an integer
- Raise it to the 1.1 power
- Raise the result to the 1.1 power
- Raise that result to the 1.1 power
- etc, etc, etc
- Keep doing that until the result is bigger than 100
- Report how many exponentiations were required to exceed 100.
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
- Write a program that prints the squares of the first 100 positive integers.
- Write a module named Square that takes one input variable and returns its square. Then rewrite your program from #1 to include this
module.
- Read about Mathematica's
random-number-generating commands. You'll need them for most of the exercises below.
- Write a program that flips a fair coin 100 times and outputs the results, like this:
tails
tails
heads
tails
heads
heads
.
.
.
- Write a module called FlipNTimes that flips a fair coin n times and returns the number of heads that were flipped.
- 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.
- Similar to #4, roll a fair six-sided die 100 times and output the results.
- 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
- 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.
- Write a module that rolls a fair six-sided die repeatedly until a six appears, and returns the number of rolls required.
- 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
.
.
.
- 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
.
.
.