CPSC 120: Notes from Class, January 27–31

Control Constructs: a lightning tour

Home | Calendar | Announcements | Assignments | Example Code | Notes

Introduction

This tutorial is a very rapid introduction to the constructs that form the core of any program written in the Processing language. We'll spend the next several weeks studying these in detail, so don't worry if you aren't fully comfortable with all the details right now. The goal here is to develop a basic conceptual understanding of these fundamental elements, to the level that you can read existing code, have a good sense of what's going on, and even experiment a little with changes.

As you read this document, you'll want to try out the examples in the Processing IDE. Run them, modify them in creative ways, and observe (and understand) what happens!

A Skeleton Program

New Terms: compiler,code block, statement, sequential execution

In Processing, a program is called a sketch. We'll expand this to include other choices later, but for now, we'll use a basic form for all of our programs:

void setup() {

   /* < your program here > /*

}

The opening { and closing } mark the beginning/end of a block of code. In that block are any number of statements, each one terminated by a semicolon (;). When you run a sketch, the Processing compiler translates your human-readable program into a form that can be run on a computer and runs the resulting program.

In Monday's class, we discussed a few of the available statements, and we used these to make the following example:

Example #1

void setup() {
  size(500,400);           // 1.
  background(10,100,75);   // 2.
  
  strokeWeight(10);        // 3.

  stroke(255);             // 4.
  point(100, 100);         // 5.
  point(175, 100);         // 6.
  
  stroke(0,0,255);         // 7.
  point(250, 100);         // 8.
  
  stroke(255);             // 9.
  point(325, 100);         // 10.
  point(400, 100);         // 11.

}

Some fine points:

Variables and Assignment Statements

New Terms: variable,declaration, type, assignment

Look at how the points are drawn in the previous example: each one has the same y-coordinate, while the x-coordinate increases by exactly 75 with each new point. Now suppose that we want to space apart our points by 50 pixels, instead of 75. What what we've seen so far, we'd have to rewrite our original program with explicit changes to lines 6,8,10, and 11. Then, if we want to try out another spacing (say, 45), we'd have to change those four statements again. It's pretty easy to see that, as the complexity of our programs increases, it will only get more difficult to keep track of all the changes that must be made, everywhere.

Instead, we can store the values of the current x-coordinate we're using and the amount of change in variables:

Example #2

void setup() {
  size(500,400);           // 1.
  background(10,100,75);   // 2.
  
  strokeWeight(10);        // 3.

  int x = 100;             // 4.
  int y = 100;             // 5.
  int changeX = 75;        // 6.
  
  stroke(255);             // 7.
  point(x, y);             // 8.
  x = x + changeX;         // 9.
  
  point(x, y);             // 10.
  x = x + changeX;         // 11.
  
  stroke(0,0,255);         // 12.
  point(x, y);             // 13.
  x = x + changeX;         // 14.
  
  stroke(255);             // 15.
  point(x, y);             // 16.
  x = x + changeX;         // 17.
  
  point(x, y);             // 18.
}

Well, yes, we now have a longer program. The thing to notice, though, is how much repetition we've introduced in the main drawing part (lines 7 - 18). When I put this example together, I just used copy and paste, counting that I'd done it enough times. If I want to change the spacing between points to be 45 instead of 50, I only need to make one change, at Line 6:

  int changeX = 45;        // 6.

It's this ability to localize commitments to particular values and cleanly express various kinds of change that makes variables useful.

Conceptually, a variable is a name for a location in computer memory where a value is stored. When we want to read the stored value or change the value that's stored, we use the variable's name: when reading (as in Lines 8, 10, 13, 16, and 18), the name gives us the value that's currently stored there. When we want to change the stored value (as in Lines 9, 11, 14, and 17), then the variable name gives the underlying location in memory where we'll store the new value.

We'll spend a lot more time with variables. For now, the important things to know are these:

Iteration (Loops)

New Terms: while statements, boolean-valued tests, definite loop pattern

In the second example above, you'll notice a lot of repetition in the code. In fact, if you deleted the stroke() statements, the end of the program is just a copy/past of the same two lines, plus one extra point():

Example #3

void setup() {
  size(500,400);           // 1.
  background(10,100,75);   // 2.
  
  strokeWeight(10);        // 3.

  int x = 100;             // 4.
  int y = 100;             // 5.
  int changeX = 75;        // 6.
  stroke(255);             // 7.

  point(x, y);             // 8.
  x = x + changeX;         // 9.
  
  point(x, y);             // 10.
  x = x + changeX;         // 11.
  
  point(x, y);             // 12.
  x = x + changeX;         // 13.
  
  point(x, y);             // 14.
  x = x + changeX;         // 15.
  
  point(x, y);             // 16.
}

Repetition of an identical or nearly-identical block of statements is more elegantly expressed as a loop (a.k.a. "iteration"):

Example #4

void setup() {
  size(500,400);           // 1.
  background(10,100,75);   // 2.
  
  strokeWeight(10);        // 3.

  int x = 100;             // 4.
  int y = 100;             // 5.
  int changeX = 75;        // 6.
  stroke(255);             // 7.
  
  int i = 0;               // 8.
  while (i < 4) {          // 9.
      point(x, y);         // 10.
      x = x + changeX;     // 11.
      i = i + 1;           // 12.
  }
  
  point(x, y);             // 13.
}

The pattern at work here is this:

while ( < boolean test > ) {
    < loop body  >
}   

In our example, the < boolean test > is the expression "(i < 4)", in Line 9. The term "boolean" refers to the fact that this kind of expression is a logical test, with only two possible values: it evaluates to true if the value of i is currently less than 4 and false otherwise. In addition to <, Processing supports a full range of other "comparison" tests: >, <= (i.e. ≤), >= (≥), == ("is equal to"), and != (≠).

The < loop body > occurs in Lines 10–12. It runs only if the < test > evaluates to true, after which the < test > is checked again. If the < test > evaluates to false, the < loop body > is skipped ("the loop ends"), and the program proceeds with the statement immediately following the loop (Line 13 in our example).

The while loop is one of the most expressive and subtlest constructs available in Processing (or any programming language), and mastering it is at the core of learning to program well. For now, it's handy to learn just one pattern, which we'll call the definite loop pattern. In this pattern, we make a loop run exactly n times, for your favorite choice of n (4 in the example above).

int i = 0;                      // initialization
while ( i < n ) {
    <"main" loop body>
    i = i + 1;                  // progress toward loop termination
}   

In our example, < initialization > is at Line 8, and for the definite loop pattern, you want to use 0 as your initial value. The variable i keeps track of how many times the loop body has completed. There's no magic about the choice of variable name, but "i" is traditionally used (also "j" and "k").

Conditional Execution

New Terms: if statements, if/else statements,

The problem with our loop version is that, by simply repeating the same drawing command (with the loop changing the values of x and i, we're now drawing all of our dots the same color. To add variation back in to the loop version, drawing a blue dot only if we're at the center one, we can use an if ("conditional") statement:

Example #5

void setup() {
  size(500,400);           // 1.
  background(10,100,75);   // 2.
  
  strokeWeight(10);        // 3.

  int x = 100;             // 4.
  int y = 100;             // 5.
  int changeX = 75;        // 6.
  
  int i = 0;               // 8.
  while (i < 4) {          // 9.
      if (x == 250) {       // 10.
         stroke(0,0,255);  // 11.
      }
      if (x != 250) {      // 12.
         stroke(255);      // 13.
      }
      point(x, y);         // 14.
      x = x + changeX;     // 15.
      i = i + 1;           // 16.
  }
  
  point(x, y);             // 17.
}

An if statement looks a lot like a loop, and it uses the same kind of boolean tests:

if ( < boolean test > ) {
    < conditional body  >
}   

The difference is that the < conditional body > in an if statement runs only once (if the < boolean test > evaluates to true) or not at all.

In Example #5, we have two if statements, Lines 10-11 and 12-13. Note that during an iteration of the loop body, only one of these can ever do anything, since the test at Line 12 is true if and only if the test at Line 10 is false. Pairs of alternatives like this are said to be mutually exclusive, and they arise so often in programming that there is a special form for them, known as if/else:

Example #6

void setup() {
  size(500,400);           // 1.
  background(10,100,75);   // 2.
  
  strokeWeight(10);        // 3.

  int x = 100;             // 4.
  int y = 100;             // 5.
  int changeX = 75;        // 6.
  
  int i = 0;               // 8.
  while (i < 4) {          // 9.
      if (x == 250) {       // 10.
         stroke(0,0,255);  // 11.
      } else {    
         stroke(255);      // 12.
      }
      point(x, y);         // 13.
      x = x + changeX;     // 14.
      i = i + 1;           // 15.
  }
  
  point(x, y);             // 16.
}

Conclusion

Whew! That's enough for this tour. Over the next several weeks, we'll study all of these constructs in much more detail, along with a few others not discussed here. For now, try out all these examples, and experiment with them. Rearrange statements. Change the tests that guard the loops and conditional statements. Try out different initial values for i. Play around with the colors. Vary the value of changeX. Add a changeY variable, and use it to vary the y position of each point. Try out different values for strokeWeight.

Try to anticipate the effects of your experimental changes: write down what you expect before running the modified code. Only then should you actually try out the modified code. If your observed results and expected results agree, that's great. If they disagree, even better: try to figure out the difference between the reasons for what you expected and what the machine really does!


John H. E. Lasseter