Teaching the humble for loop

34

3

So, in C-style languages, the `for` loop has a funny little syntax:

``````for (int i = 0; i < 100; i++){
// do something
}
``````

If we order these sections in a natural, intuitive fashion, we would get something like:

`````` for (1; 2; 3){
4
}
``````

... which is how students seem to feel like things should be ordered. But, of course, the real execution order is `1, 2, 4, 3, 2, 4, 3, 2, 4, 3, 2, 4, 3...`

What tricks have you developed for helping students make sense of `for` loops at the very start?

Teach them to learn what things mean instead of guessing from how they look. – philipxy – 2018-02-23T23:16:29.260

In the C-style languages is it allowed to let step be a default? I.e., is `for (int i = 0; i &lt; 100; ) {` legal and intrepreted as increment `i` by one each step? – Gypsy Spellweaver – 2017-07-13T22:48:50.177

8Actually that is legal, but is an infinite loop, with i always 1. Also: defaults of all kinds are frequent sources of error. Make your programs explicit. – Buffy – 2017-07-13T23:15:16.013

I asked, because frequently parameters are often set up so that they can be dropped (defaulted) in reverse order (right-to-left). In the case of `for`, if that's part of the logic in creating the syntax, it can be given as `for (int i = 0; i &lt; 100; ){`, or `for (int i = 0 ; ; ){` but not `for (int i = 0 ; ; i++) {` because the center term is empty, while some term to the right is not empty. – Gypsy Spellweaver – 2017-07-14T00:12:00.007

5@GypsySpellweaver any or all of them can be empty, in any combination, but they all default to nothing (respectively: no initialization, no termination except by explicit `break`, and no post-loop-body expression). – hobbs – 2017-07-14T05:01:37.400

@hobbs I was suspecting that they were independently "empty-legal" since they are, in essence commands (with the `;` separator), but the habit of what makes sense to the language creators for a parameter list probably had its influence. Having started with a different language, with explicit syntax would help the students. Something like `FOR int x = 1 TO 100 STEP 1` leaves no doubt, while being less adaptable. – Gypsy Spellweaver – 2017-07-14T05:10:51.197

2Can you add some specific problems your students seem to have with learning the for loop? – AnoE – 2017-07-14T13:43:33.463

1@GypsySpellweaver - No, there is no default behavior to a C-style for loop. The three parts are valid C expressions that don't necessarily have anything to do with indexing a variable. The expression could read another line of input from a file and the exit test could be checking for <EOF>. It is very flexible and powerful, but you must tell it exactly what you want it to do. – MrWonderful – 2017-07-14T18:58:11.047

I got a much deeper understanding of `for-loop`s when my teacher started leaving `1`, `2`, or `3` blank, switched the variables in `1`, `2`, and `3` from `ints` to `booleans` (or even `Strings` in Java), or made loops that required logic in `4` to make `2` true (and `3` was obsolete). I started to understand what each part meant and how to use it, instead of assuming `for-loop`s were always "increment an int until it hits a certain value". – Lord Farquaad – 2017-07-14T21:32:39.470

1A for loop in c style syntax is a shortcut for a while loop with post operation (increment being a common case). Show the expanded while form, then show the for form. – pojo-guy – 2017-07-15T12:41:47.477

2This is why you shouldn't teach basic programming using C. It makes it extra hard to grasp the concept itself... – vacip – 2017-07-17T11:00:27.593

16

I know I'm coming late to this party but this is a really good question and I think it deserves a really good treatment.

The original `for` loop, the one with the semicolons in it, is amazingly flexible, powerful, and confusing as all get out. It is seldom taught well, it is a very easy place for bugs to hide, and it is not going away.

To master it you must learn the 1,   2, 4, 3,   2, 4, 3 pattern the OP mentioned. You must burn it into your soul. You must burn it into the souls of your students. The question is, how?

The best way is to actually see it. To watch it happen. Unfortunately debugging tools often insist on highlighting the entire line.

Can you tell if `i` is about to be initialized, tested, or incremented here? Debugger highlighting is often useless for teaching or debugging the classic for loop.

However, there is a way around that problem. The c style languages all have a feature that lets us work around this debugger limitation. Whitespace. Whitespace is meaningless in these languages. You can take advantage of that.

So long as you're willing to explain that this:

``````for(int i=0; i<10; i++) {
System.out.println(i);
}
``````

will do the same thing as this:

``````for(
int i=0;
i<10;
i++
) {
System.out.println(i);
}
``````

then you can show them this:

Do that, and rather than just explaining, you are showing them how this works, you are teaching them how to debug it themselves when they get confused by it.

Now sure the multiline style isn't popular with for loops but the students certainly don't have to turn in code that looks like that. It's just a form to use while testing because our debugging tools aren't yet smart enough to highlight only the relevant parts of a line.

I've changed the speed and rhythm I use as I click. That's no accident. I'm using that to emphasize the starting and ending parts of each iteration. Students should not only feel like they can predict this. They should feel like they can use the debugger to re-teach themselves this pattern quickly if they ever need to.

Consider a cascading style that retains column position for those more visually oriented:

Now that's just the start of understanding loops. Writing correct, readable, efficient loops is a whole other issue that this answer hardly does justice to.

A lot of bugs hide right here. Don't undersell how worthy this is of close study.

1You may be late to our "party" and I haven't noticed you here before, but you are giving some great answers. I'd offer you a party hat if I had one. Oh, I do, +1. – Buffy – 2018-02-07T12:55:01.093

@Buffy hehe, thank you. This isn't my usual haunt but I can't resist a good question. – candied_orange – 2018-02-07T14:30:59.050

2This just might be the most useful answer I've seen in the last 3 months. – ItamarG3 – 2018-02-08T13:28:39.923

52

Show then how the loop can be unfolded into a while loop:

``````int i = 0 // Step 1
while (i< 100) { // Step 2
// Do something
i++ // Step 3
}
``````

Explain that the order is derived from that unfolding.

You could start out by only teaching while loops, forcing them to make these while loops with iterators, then showing that for loops are a condensed of those while loops with incrementing variables.

About the cliché "for loop are syntactical sugar around while", it must be noted that the C for-loop was present already present in B https://www.thinkage.ca/english/gcos/expl/b/manu/manu.html#Section5_7_4 together with a `forever` loop which is leaved by break or goto. So the while loop is a shortcut for `forever { if (! condition) break; .... }` if you are sugar addicted.

– Michel Billaud – 2018-02-07T10:33:21.997

And, actually, `while(cond) instr` is syntactic sugar around the special case `for(;cond;) instr;` as it preserves the meaning of a `continue` statement, not the other way. The fact is, our opinions about which is more fundamental may be biased when we prevously taught Pascal where there is no equivalent to the C-like for loop. – Michel Billaud – 2018-02-09T07:56:52.617

8…and be prepared for the question "Why should we use `for` loops instead of `while` loops when they're doing equivalent things, what's their advantage?" – Bergi – 2017-07-14T01:34:49.990

2@Bergi You're telling the compiler what you want more clearly, allowing more optimization, and they simply take up less space and look neater. – thesecretmaster – 2017-07-14T01:35:51.150

19@thesecretmaster "Required to tell the compiler…" has never been a good reason. And I can only hope that the syntax choice does not affect the optimiser at all. The point is that they are easier to understand for the programmer(s) that have to read them. The iteration variable is (supposed to be) initialised and modified only in that single line, which makes for a an easily recognisable pattern and correct code where the update step is not forgotten. It also works with `continue;` – Bergi – 2017-07-14T01:45:11.063

6@Bergi Keeping the loop control variable in the `for` scope is a practical advantage as well. The more correct "expansion" of a `for` loop would be: `{int i = 0; while(i &lt; 100){++i;}}`. – IllusiveBrian – 2017-07-14T13:51:26.083

In C# and C the declaration of the iteration variable ('i' in this case) can be inside or outside the opening parenthesis of the for statement, which means the scope can be only within the loop, or outside as well. This is a feature, not a bug: if you need the scope to extend beyond the loop, you can, and if you don't you don't need it to. I introduce while loops first, then explain for as an easier to read syntax. – None – 2017-07-14T16:18:59.213

@IllusiveBrian creation of the loop variable inside the `for(` statement wasn't possible until C99 allowed it. – Ruslan – 2017-07-15T14:24:35.310

For loops do not optimise better than while loops (at least in gcc, clang and visual studio in C and C++). Arguments about easier to read code are sensible. – Chris Jefferson – 2017-07-15T17:50:15.630

Won't this lead to teaching the Duff's device? That's not a path I'd recommend with students this fresh. – Mast – 2017-07-15T19:28:50.913

I would like to add that, even though the while and for loops may be the same, in practical terms I would favor new programmers using the for loop, as it makes it much less likely to accidentally produce an infinite loop. – bytepusher – 2017-07-16T21:07:17.817

25

I teach `for` loops using the following pseudocode, which we then translate into actual code:

``````for (initialization; condition; update)
{
statement;
}
``````

This logic helps justify why the order goes `1, 2, 4, 3` to start. I explain it as follows: a variable can only be initialized once so it happens first and never again. After that the variable is tested against the condition. If the condition is true, then the code proceeds to the statement. Only then will it reach update. If the first conditional test fails, the update code is never executed.

I have found that `for` loops are one of the few constructs that benefit most directly from drawing a flowchart. Using the visual diagram will help this logic become clearer.

Another benefit of teaching the `for` loop this way is that it becomes trivial to translate it into `while` or `do-while` with the same pseudocode constructs.

``````// while loop
initialization;
while (condition)
{
statement;
update;
}

// do-while loop
initialization;
do
{
statement;
update;
} (condition);
``````

Once I do this, I can focus on each of these four parts, so students understand the different components that make up a loop regardless of the structure thereof.

1That's a clear way to explain loops. I use a similar approach. – Edwin Torres D.Eng. – 2017-07-14T15:31:54.893

The do while and while loops stated aren't exactly equivalent. If after initialization, condition is false, then you never execute statement+update in the while loop, but in the do-while loop, you always execute statement+update at least once. This could easily lead to confusion. – Batman – 2017-07-15T21:58:46.353

@Batman Absolutely. That would be part of a broader instruction on the differences between and among these three loop constructs. Nevertheless, the general pseudocode holds true. – Peter – 2017-07-15T22:05:44.260

1This is pretty much how I do it but I'm a little more explicit in each section: `pre-loop-initialisations`, `condition-for-doing-iteration`, `post-iteration-actions` and `iteration-actions`. Once they understand that the `iteration` is a single execution of the body and the `loop` is the entire thing, this seems to be easy to pick up (even for teens). – None – 2017-07-17T01:49:20.447

9

It's about setting boundary conditions first. Provide them with a more concrete example, like filling a glass of milk. You kind of need a glass or container (let me know the next time you pour milk right on the counter) and that glass is some amount full. We'll just treat it as empty. Then, you stop filling the glass when it's full (or half full or whatever), unless you want milk everywhere.

Within those boundary conditions, you pour the milk jug. That is, mentally, you're thinking (in not so many terms):

``````repeat following until checks make you stop pouring milk
check: fullness of glass
check: how close the fullness is to the wanted end fullness
``````

That's exactly how a for loop thinks. It needs your conditions first, and then what you want to repeat while those conditions are true. (Yes, while - just like a while loop. I personally agree with thesecretmaster in that it makes a lot of sense to introduce while loops before for loops.) As you're pouring, you're constantly rechecking the level of the milk - "updating" a variable in your head as you act. So, if you were asking a computer to fill a glass of milk:

``````import milk
for (float glassFullness = 0; glassFullness < 1; glassFullness+=0.1) {
milk.Fill()
}
``````

That could, of course, also be written

``````import milk
float glassFullness = 0;
while (glassFullness < 1) {
milk.Fill()
glassFullness+=0.1
}
``````

They're doing the same things; the for loop is just a condensed version.

Tl;dr: a for loop looks for the boundary conditions, and then does what it needs to do, rechecking with a redefined variable accounting for the change that has been made. You input what change needs to be made to the variable to account for the action.

Boundary conditions are very helpful in explaining loops. – None – 2017-07-14T16:21:04.407

8

tl;dr- A `for` loop is just a `while` loop that has extra slots for a pre-loop statement and a post-loop-run statement. Since they're pretty much the same thing just written differently, a `for` loop is best understood as syntactic sugar that's useful when it increases the readability of the code.

Sometimes new programmers are worried about performance, thinking that syntactic sugar might be less efficient. That's not the case; the compiler really doesn't care how you write these loops, it's all the same to it. So, students should be told to just write whichever's cleaner in the current context.

Homework/test question idea: Have students convert a `for` loop into an equivalent `while` loop, or a `while` loop into an equivalent `for` loop.

Mapping between `for` and `while`

The `for` loop:

``````for (prestatement; condition; poststatement)
{
//  loop body
}
``````

is equivalent to the `while` loop:

``````{
prestatement;
while (condition)
{
//  loop body
poststatement;
}
}
``````

, where:

• `prestatement` is an executable statement that happens only once, before the loop starts.

• If omitted, then the loop just doesn't do anything extra before starting.
• `condition` is a `Boolean` expression that's evaluated at the start of each loop execution, breaking if `false`.

• If omitted, then there's no break check there.
• `poststatement` is an executable statement that happens after the end of each loop execution.

• If omitted, then the loop just doesn't do anything extra after each execution.

Note on scope

Even though

``````int i = 0;
int i = 0;
``````

is illegal code because we can't declare `i` twice in the same scope,

``````{
int i = 0;
}
{
int i = 0;
}
``````

is legal because both `i`'s are in different scopes.

This scoping issue is why we have the `while` statement above wrapped in extra `{}`'s, since the `prestatement` belongs in that scope.

`for` loops can omit parts

Might seem weird at first glance, but

``````for (;;) { }
``````

is actually perfectly legal code! Since it omits a `break` condition and doesn't do anything, it's basically an infinite loop that runs forever without doing anything.

In C#, it doesn't consume CPU time, but it also blocks the thread from progressing. For example,

``````for (;;) { }    //  Infinite loop
foo();          //  Never happens because the above loop never ends
``````

Not only syntactical sugar around a `while` loop, as the `continue` statement of the `while` loop skips the "poststatement". – Michel Billaud – 2017-07-19T19:48:38.823

7

Theoretically, I teach the while loop first. Others have already explained how and why, but I'll go ahead with an example:

``````void showRating( int rating )
{
int i = 0;
while ( i < rating )
{
putchar( '*' );
++i;
}
putchar( '\n' );
}
``````

Some of the examples will be better expressed as for loops, and that's where I introduce the for loop, as a way of containing the essential elements in a concise construct. I might ask what happens if I forget to increment i:

``````void showRating( int rating )
{
int i = 0;
while ( i < rating )
{
putchar( '*' );
}
putchar( '\n' );
}
``````

And we compile it and run it.

Then I show how the for loop might help us not forget the counter:

``````void showRating( int rating )
{
int i;
for ( i = 0; i < rating; ++i )
{
putchar( '*' );
}
putchar( '\n' );
}
``````

Too often, people think first of counting when they think of loops, and that's why they jump to the for loop first. But the C for loop is not really the counted loop that BASIC has led us to believe it is. And the English grammar for the word is not limited to counting, either.

So I'll throw some for loops in that don't strictly count.

I also teach the pun on forever that used to be the standard flag for an indeterminate loop:

``````for( ;; )
``````

I'm likely to teach the post-test do ... while loop after having shown an indeterminate loop with a break at the end:

``````for ( ;; )
{
/* Always executes once. */
if ( condition ) break;
}
``````

Now I would want to use something concrete here so I can show that it really always executes once, but I don't have time to make something up today.

Then I can show them the do .. while loop:

``````do
{
/* Always executes once. */
} while ( !condition );
``````

Somewhere along in here I'll introduce the faux style thing about bracket placement and warn them not to argue with the managers about where to place them.

Great set of variations. And, it is not good to argue with Visual Studio about how to format the code either! – None – 2017-07-14T16:26:21.040

7

I think the way to teach it is not by talking about order of operation, but by teaching the overall concept of a looping. The `for` header contains the steps that are generally common to looping: initialize iteration variables, test whether the loop should proceed, and update variables between iterations.

As others have suggested, you could start by teaching `while` loops, since they have simpler structure. Then you could point out that many of these loops have some common structure: they initialize a variable before starting, and update that variable at the end of the body. This is so common that we have a special `for` statement that encapsulates it.

This is also useful as an illustration of modularity, which is a cornerstone of good programming. Whenever you find yourself doing similar things over and over, give that thing a name and encode it as a function, macro, structure/class, etc. In this case, it's just happening at a higher level, in language design: much of the evolution of languages over the decades has been finding common programming patterns and creating languages that have them as built-in operations and/or functions (e.g. `foreach` in PHP).

1Welcome to [cseducators.se]! I hope we hear more from you in the future. – Ben I. – 2017-07-14T21:22:03.853

6

I used to have a role where I supported college students in CS1. I developed the following exercises to address this exact issue:

Counting code executions

Text version:

How many times do each of the following pieces of code execute?

``````int sum = 0;
int i;
for(i = 1 /*A*/; i < 5 /*B*/; i = i + 1 /*C*/)
{
sum = sum + i; /*D*/
}
``````

Hint: translate a while loop -- the pieces should execute the same number of times as the do here.

Translating loops

Work on each of these individually first, then partner up. Switch partners between exercises.

A. Convert this while loop into a for loop:

``````int k = 0;
while (k < 7) {
cout << "Hola " << k << end1;
k = k + 2;
}
``````

What is the output of the loop?

What is the value of `k` after the loop completes?

B. Convert this for loop into a while loop

``````for (int countdown = 10; countdown >= 0; countdown--) {
cout << "T-minus " << countdown << end1;
}
cout << "BLASTOFF!!!" << end1;
``````

What is the value of the countdown after the loop completes?

If you teach very well maybe the questions are too easy, but in my setting they led to a lot of improvement. Notice that I included a counter increasing by something other than 1, and a loop counting downwards, since my students often didn't see examples like that in class.

Hyper-clarity is teaching well. This is an excellent exercise. Welcome to [cseducators.se]. I hope we hear more from you in the future! – Ben I. – 2017-07-16T22:14:24.233

1This isn't just an answer; it also has useful material in it that'd be of general value to educators. At current, SE.CSEducators doesn't really have a good mechanism for promoting this content, though there might be an argument for cloning StackOverflow's recent Documentation feature here - just, instead of being documentation on programming, it'd be a repository of teaching material. – Nat – 2017-07-16T22:53:18.007

1@Nat That actually sounds like a fantastic idea! If you want to, create a meta question requesting it. It's certainly unprecedented, but it's worth asking :) – thesecretmaster – 2017-07-16T22:59:36.487

@Nat (and nova) I forgot to mention, come by the chat room. We'd love to meet you!

– Ben I. – 2017-07-16T23:00:43.923

4

I am not an educator, but I hope my answer will be helpful.

In B.A.S.I.C, which I learned as a child, there are "for-to-step" loops and their argument order is the same as C style languages. I have to remember them like this in order to keep it right for myself.

This is QBASIC

``````FOR counter = start TO end [STEP]
[Statement Block]
NEXT counter
``````

or

``````FOR i= 100 TO 0 STEP -1
PRINT i
NEXT i
``````

This helps me personaly understand what's happening in those loops.

Another thing you might try is an alteration of what you posted above:

`````` for (1; //initialize
2; /* are we done? */ 4 /*increment*/){
3 //do the work
} // finish
``````

Hi TecBrat! Thanks for sharing you experience and welcome to [cseducators.se]! – thesecretmaster – 2017-07-14T14:44:16.680

4

Maybe the

``````for (int i=0; i<10; i++)
{ ... }
``````

loops seems funny when you compare it to the "interval" loop of other languages, like Pascal's

``````for I:=1 to 10 by 1 do
``````

but the C for loop was designed as a much more general linguistic mechanism than loops with counters.

The objective was to keep all elements of the "control" of the loop together on one side for better maintenance, and the body of the loop on the other side.

So we have a lot of common idioms, like

`````` for (char *ptr = string; ptr != '\O'; ptr ++) {....}

for (struct Cell p = first; p != NULL;  p = p->next) {  .... }
``````

The loop over an interval is only an instance. Maybe the problem is in the overuse of counters in the exercices for beginners (even when the range-based for loop is available). A survival from good old Fortran programming :-) ?

Nice point. And welcome to [cseducators.se]! – Ben I. – 2017-07-19T20:25:42.113

3

However, don't neglect the fact that the first and last of the three sections between the parentheses can have (in C, anyway) multiple statements separated by commas (comma is an operator in C).

``````int i, j;
for(i = 0, j = 100; i < 100; i++, j = 2 * j + i){
//do something strange
};
``````

You can write quite a lot of a program in a single `for` statement. The final section needn't just increment or decrement things. Arbitrary complexity is possible. Probably not a good idea to encourage it, however. But students reading code of others may come across it, so should be aware.

One use of this is to iterate over a two (or more) dimensional array in a single `for` statement.

Because of the complexity, it is probably best to show how it maps to a while statement as suggested in another answer here

Is the order of evaluation in your "step" even defined? – Džuris – 2017-07-14T03:14:02.213

1@Džuris yes. There is a sequence point between the evaluation of the left and right hand sides of the comma operator. – hobbs – 2017-07-14T05:06:13.967

(but calling it "multiple statements" isn't accurate. It's a single expression. And the `int i = 0, int j = 100` part actually isn't valid because that's not how declarations work, though it would be without the second `int`). – hobbs – 2017-07-14T05:08:49.587

@hobbs oops and Thanks. Corrected to remove the declaration from the loop. – Buffy – 2017-07-14T09:08:49.690

Yay for the comma operator! I have used it to achieve some significant and very necessary functionality in the past, which I could not do otherwise (trying to fit a macro in place of a function, what is called 'inlining' these days). – None – 2017-07-14T16:23:35.680

3

I love thesecretmaster's answer of showing the mapping between the two loops (and I will almost certainly do that in the future). However, as an AP Computer Science A teacher, I teach in Java, and there is a subtle trap in saying that

``````for (int i = 0; i < 100; i++){
// do something
}
``````

is equal to

``````int i = 0;
while (i< 100) {
// do something
i++;
}
``````

The two loops are identical, but of course there is one subtle but important difference: the scope of `i` (and its corresponding pollution of the namespace). Java considers the int declared in the first section of the for-loop header to be declared in the loop, and therefore out of scope once the loop ends.

The `while` loop is really equivalent to this loop in C:

``````int i;
for (i = 0; i < 100; i++){
// do something
}
``````

If you are to take the path of mapping equivalencies between the loops, you really should also point out this trap.

anyway, a `continue` statement in the body breaks the equivalence of the 2 pieces of code. – Michel Billaud – 2018-02-10T16:59:54.967

1

You have a good point about the scope issue, though that's less a difference and more an issue of an incomplete translation. As noted in @IllusiveBrian's comment, `{}` need to be included to control for scope - which may look unusual since most people don't care enough about scope, but it's needed to maintain equivalence.

– Nat – 2017-07-14T15:08:50.790

1For example, the `int i = 0; while (i&lt; 100) { /* do something */ i++; }` above should be `{ int i = 0; while (i&lt; 100) { /* do something */ i++; } }`. – Nat – 2017-07-14T15:11:03.040

1TIL about the purpose of extra code braces. Thanks! – Ben I. – 2017-07-14T15:12:10.687

Sometimes you want to be able to see the final state of the loop control variable outside the body of the loop. I teach that also. "Students do the darndest things." – None – 2017-07-14T16:28:35.380

scope and pollution can be limited by a pair of curly brackets around declaration + loop. – Michel Billaud – 2017-07-19T20:14:56.190

2

Another way of looking at this is to explore what is missing or lost at various stages:

In Java

For each: `for( String s : < some iteratable collection > ) foo();` loses the index of the loop. It never has one unless you add it.

`for (int i=0; i< n; i++) foo();` loses the index after the loop when completed,

while other loops preserve it.

Many of the posts offer good advice for explaining the behaviour of the `for` loop. Translations to and from `while` loops are traditionally used. Strictly speaking a `for` loop is never required, it's a convenience, as is the for each loop.

The more tricky uses of a for loop should be very carefully considered before teaching -- high risk low reward.

I agree about de-stressing low reward features. However, they can't be neglected entirely as students (and professionals) still need to read the code of others who might not exhibit such "sanitary" practices. – Buffy – 2018-02-06T15:17:25.160

I would add that it is also heavily tested on the AP CS A test, where they love to introduce off-by-one errors and tricky ending conditions. – Ben I. – 2018-02-06T16:58:32.427