Thursday, January 23, 2014

The Many Ways to Write a For Loop

For our loops curriculum, we start off with introducing while loops, because of their more intuitive form. We then introduce for loops in a talk-through where we describe them as a shortcut syntax for common while loop scenarios, and like with all of our curriculum, we present the students with a coding challenge after to get them actually using for loops.

In this Lined Paper challenge, we start off with a while loop and ask them to convert it to a for loop. The beginning code is:

var i = 0;
while (i < 20) {
    var lineY = 20 + (i * 20);
    line(0, lineY, 400, lineY);
    i++;
}
And the hint code is:
for (_; _; _) {
    var lineY = 20 + (i * 20);
    line(0, lineY, 400, lineY);
}

For the solution, we're looking for them to rearrange the components of the while loop into a for loop, which you can likely do easily if you're reading this.

For all of our challenges, we start them off with a fairly strict StructuredJS-based grader - just looking for the most obvious solutions that we come up with when developing the challenge. But then we release the challenges into the wild, monitor the feedback that comes in through the "Help" link, and adjust the graders. Sometimes that means adding more successful match patterns for students that came up with valid alternate syntax; many times that means adding more messages to help students understand what's wrong with their syntax.

It's always fascinating to see the many ways that new and experienced programmers come up with to write what is effectively the same program, especially in JavaScript because it is a language with multiple overlapping syntaxes - so I thought I'd share some of the for loops variations here.


Ways to get it wrong

Some learners don't quite understand the order of expressions in the for statement:

for (var i < 0; i < 0; i++) {
}
// Warns "The first expression in your for() should be an initialization of your counter variable, *not* a conditional check."

A few learners put their initial variable definition outside of the for loop - which works, but defeats 1/3 of the point of the for syntax, which is what we're trying to teach:

var i = 0; 
for (i; i < 20; i++) {
}
// Warns "It looks like you're initializing your counter variable before you create the for loop. As best practice, you should be doing that as the first expression in the for()."

A few learners came up with creative new syntaxes for adding one to i, which we added regex-based messages for (since the code can't be analyzed as valid JS):

i+++   // Warns "The increment operator is only 2 plus signs after the variable name, not three."
i ++ 1 // Warns "It looks like you were trying to use the += operator but wrote ++ instead."

Of course, learners also struggle with the logic of the for statement, finding all sorts of ways to produce an infinite loop, which we unfortunately can't give helpful messages for yet, due to the way our error messages are produced:

for (var i = 0; i > 360; i++) {
}
for (var i = 0; i < 20; i++) {
  var i = 0;
}

Ways to get it right

Perhaps more interesting is the way that people got it right that I did not anticipate, due to my own deep-seated conventions.

Typically we use i++ in our talk-throughs, but many programmers prefer += 1. Some argue it's more intuitive, some like how easy it is to change the value, and others, like Douglas Crockford, discourage using the increment/decrement operators and bake that into linting tools. So, we now accept all of these for the increment expression:

i += 1 
i++
++i

Note that ++ and += are not functionally equivalent in terms of type casting, as pointed out by Nikolas Co.

var s = "hi";
var t = "hi";
s++; // NaN
t += 1; // hi1

And of course, a post-increment operator works slightly differently than a pre-increment operator. But, in the case of this for loop, where we have already checked that the value is a number, the difference between these operators doesn't matter.

If you're curious, here is what the constraints look like for making sure that their expression is one of the accepted ones, analyzing the AST node that we parsed with Esprima:

var isPlusPlus = function(ast, varName) {
    return ast.type === "UpdateExpression" &&
        ast.operator === "++" &&
        ast.argument.name === varName;
};
var isPlusEqual = function(ast, varName) {
    return ast.type === "AssignmentExpression" &&
        ast.operator === "+=" &&
        ast.left.name === varName &&
        ast.right.value === 1;
};

It is fascinating to teach programming and come up with these challenge graders, because it reminds me of the many ways that new programmers can be confused about a syntax and challenges my ideas about what the right way is to program something.

No comments:

Post a Comment