Friday, November 14, 2014

Going Loopy a While

Another key thing you need to understand is looping.  Repeatedly performing identical operations is extremely common in any programming language, and Java supports a pretty full set of looping structures.

Perhaps the simplest loop to understand is the while loop.  Let's write a simple one:

boolean keepRunning = true;

while ( keepRunning )  {
    System.out.println("I am in a loop.");
    keepRunning = false;
System.out.println("I am no longer in a loop.");

Now, the above code was the most pointless of all possible while loops, since it executes its code just once and stops.  Specifically:

First, we created a boolean variable and set it equal to true.  We are using this as a flag or condition for the loop.

Then we created a loop, which will execute over and over again so long as keepRunning is still true.  The while statement always expects a boolean expression in the parentheses, and executes one statement so long as that expression evaluates to true.  Curly braces along with any code inside of them count as one statement, and I strongly urge you not to skip them just because some looping or conditional structure you're using only has one line.

So the while statement executes, sees that its condition is true, so it enters the loop.

The system generates the following unexpectedly exciting output to the console:

I am in a loop.

And then it sets keepRunning to false.  The first iteration of the loop is now finished, as we hit the closing brace.

Now the program flow goes back up to the while statement.  keepRunning is now false, so the while statement will bypass the next statement (which, as I mentioned, is everything within the curly braces).

So now we pass control out of the loop and generate the following even more interesting output:

I am no longer in a loop.

That's a lot of words to describe something that is conceptually very simple, which is basically true of many programming structures.  We create instructions and that computer really has to scurry along doing a lot of different stuff for us.  Ah, the power!

Of course, a loop that doesn't loop hardly qualifies for the name, so let's do something just a bit more complex:

boolean keepRunning = true;
int runCount = 0;

while ( keepRunning ) {
    System.out.println("I am in a loop.");
    runCount = runCount + 1;
    if ( runCount > 4 )  {
        keepRunning = false;

Try to predict what will happen here.  Follow it through, it's not really that complicated.  I'll wait.

OK, there's not going to be a quiz, I'll just show the output:

I am in a loop.
I am in a loop.
I am in a loop.
I am in a loop.
I am in a loop.

Do you understand what happened here?  If so, you can skip the next bit.

We started off with two variables, keepRunning and runCount.  Let's examine the status of these during each iteration of the loop.

keepRunningrunCountWhat does loop do?
true0Prints I am in a loop.Increase runCount to 1
true1Prints I am in a loop.Increase runCount to 2
true2Prints I am in a loop.Increase runCount to 3
true3Prints I am in a loop.Increase runCount to 4
true4Prints I am in a loop.Increase runCount to 5 then sets keepRunning to false
false5Nothing! We never execute at this point, having failed the while condition

Make sure you understand the flow of that loop completely.  When it makes perfect sense to you, and you understand why everything is happening, you're ready to move on.  to the next section

I wrote that loop in pretty much the longest possible way, because I wanted the flow to be explicitly laid out and easy to follow.  Structures such as boolean flags and counters do get used, but it's rare to have two of them for a job this simple.  Most good programmers try to keep their code terse and tight, without extra fluff.  In this case, we have fluff.

If I were to write the above for myself, and I absolutely had to use a while loop to do it, I'd certainly make use of the following shortcuts:

  1. The '++' operator.  This increments a value.  There's also a similar '--' operator.  I could replace 'runCount = runCount + 1' with 'runCount ++'.  My fingers will thank me, as will those reviewing the code and really expecting to see that.  Just a caveat, the increment operator can appear before OR after the variable, and if it's alone on a line it does not matter how you use it.  But if you are using it in a larger expression it matters.  If you use it as a prefix operator by putting it before the variable, the variable is incremented and then it is evaluated.  If you put it after, the variable is evaluated and then it is incremented.  SO:

    If you write:

    int i = 5;
    int product = ++i * 5;

    Then product will be 30, because it would turn to 6 before being multiplied.

    Whereas if you write:

    int i = 5;
    int product = i++ * 5;

    Then product will be 25, because it would still be 5 when multiplied.

    In either case, after the second line i will have the value 6.
  2. I would take advantage of the boolean nature of comparison operators.  In other words, I can use the value of:

    runCount > 5

    anywhere I need a conditional.  In short, I don't need 'keepRunning' at all.
Instead, I could write my loop this way:

int runCount = 0;

while ( runCount < 5 ) {
    System.out.println("I am in a loop.");
    runCount ++;

This is cleaner, shorter, and quite clear once you understand all the bits and pieces.  Indeed, it may be clearer, as there's less to track, and the exit condition of the loop is explicitly stated right where it's used.

You have no doubt noticed by now that I have been starting everything at 0, not 1, which may feel a bit strange.  This is just something you will have to get used to, as it is extremely common for developers to do things that way.  This is most likely due to the way arrays (and pointers in some languages) function, where the first element is zero.  We'll talk about arrays in another post.

Naturally, we sometimes get the counters a bit wrong, perhaps we compared against the wrong value, or used an equals check when we should have used a less than check.  Most of the time, we get this wrong by one.  Always check your logic and indexes thoroughly, particularly before it feels really natural to you.  We call these off-by-one errors fencepost errors.  The reason for this comes down to this:  Imagine you go to your local Home Labyrinth store because you want to put up a hundred feet of fence.  The fence panels are 10 feet wide and you need 10 of them.  You also need posts to go between each panel, and because you haven't had your coffee yet (they open early and you're feeling ambitious this Saturday) you also buy 10 posts.  Everything is going great until you reach the end and realize you need an eleventh post at the far end.  (Usually this happens three minutes after the store closes, in my experience).

There are several other loop structures as well, which I'll discuss next time around.  I want you to get this simplest one locked down firmly before we move on.  It would be possible to only use this one kind of loop and write almost any program, but some of the others are better suited to certain tasks.

View code here

No comments:

Post a Comment