Spring break is next week, so I’m predictably buried in work. I am still working with my students to master loops, and I’m learning a lot about how students think about loops. In education this is called pedagogical content knowledge (PCK) – not just pedagogical knowledge (ie. how to teach), not just content knowledge (ie. how to program), but knowledge of how to teach programming. Here’s a list of PCK I’ve gained in the last three weeks:
- Looping over indices of a list, as opposed to looping over the elements of a list, confuses students. I think this is one advantage of C++ or Java over Python: there is exactly one way of looping through a list. At least, this was the case before the
foreachsyntax was introduced to either language; I don’t know if that syntax is used in introductory courses.
In make sense in hindsight to stick with one kind if loop, but I have a hard time saying which kind it should be. I have read that students find operations on sets easier to understand, but this may get confusing if students have to get things out of a list. I also know a student who prefers looping over indices, but that’s closer to anecdote than data (as though this whole blog is anything but).
- Related to this, dictionaries should be not introduced until students are comfortable with lists and loops. The relationship between the key and the value, and the intricacies of each (for example, that keys are unique but values are not) only confuse students. In fact, I’ve found that students are more confused by dictionaries than they are by nested loops.
- While loops don’t seem to be as confusing, although there are not many uses for while loops in Python. The only time a while loop has been necessary is getting verified input from the user, and I provided that for the students. The only other time it has come up was with a Collatz sequence example, but outside of such numerical examples where you wait for a value to converge, most while loops can be written as for loops instead. This means that while loops can be introduced later without affecting what students can do.
- Students often want to do too much at once. For example, when counting how many times different things occur, they often want to get this number in one step, instead of incrementing the number every time. I suspect this is related to the first point in this list, namely, that Python offers too much choice. Python lists have a
.countmethod that students want to use, even if that function doesn’t do what they want; C++ arrays have no such convenience method. As a result, students learning C++ know immediately that they have to loop, while students learning Python waste time figuring out whether the function is the right one to use.
The tradeoff here, which I haven’t found the equilibrium to, is how to allow students to efficiently write cool programs while also becoming competent with loops. This semester I created a worksheet that asks students to write their own version the library functions, which gives students practice with loops while conveying that they’ll survive without the library functions. I’m not convinced that this is the best approach.
- Another reason for doing too much at once is that accumulating values incrementally is unintuitive. Creating a variable outside a loop that keeps changing does not come naturally to students. This applies to the counting example, but also to building up other values (lists, strings, etc.). I’m not entirely sure how students tackle this problem, but it seems to be something about the reassignment of the variable in the loop.
- A different conceptual block is how to convert repeating code into a loop, and in particular, how to update values in preparation for the next loop. For example, students could write an unrolled program that calculates the Fibonacci sequence, but they would use a new variable for every term, and it takes prodding for them to see how you use shuffle values to turn it into a loop. I’m inclined to say that it’s something about seeing the terms absolutely (ie. the 6th Fibonacci number is the sum of the 5th and the 4th Fibonacci numbers) as opposed to relatively (ie. the n-th Fibonacci number is the sum of the n-1-th and the n-2-th Fibonacci numbers). I don’t quite see how this will help me teach in the future, but now I wonder how high school math teachers introduce sequences.
- The final difficulty I will mention here is not strictly about loops: students have trouble seeing whether they should be ruling things out or counting things in. In loops, one example is writing a function that checks if a list only contains odd numbers; the idea that they can quit (and return false) when they hit an even number does not occur to them. The opposite scenario (whether a list contains at least one odd number) is equally difficult.
Outside of loops, this manifests in most Boolean functions. For checking if a year, month, and day is after a particular date, both ruling things out and counting things in is equally hard. For checking if a coordinate is within an area, the shape of that area determines which one is easier – but being able to estimate the difficulty is a skill that students haven’t built up yet. This causes them to struggle with the problem, even if they know how to write the code once the correct approach is pointed out to them.
The last four points in this list are patterns in how loops work – accumulate values incrementally, keep previous values in variables, and quit early if possible. But even after a full hour of Googling, I could not find a single resource that talks about these patterns. This took me by surprise, since we have design patterns for more abstract programming concepts. I use an interactive online textbook for the class, and that doesn’t have a section on these patterns – or for that matter, how to think with loops. Neither do several other textbooks I checked.
Why not? It’s generally acknowledged that the hardest part of programming is not the syntax, but the abstract problem solving necessary to translate thought to code. (I tell my students that if they understand the question, and can get the answer by hand, they know how to solve it; the hard part is slowing down and figuring out what’s going on in their head.) So it seems like textbooks should directly address the thinking necessary – but I haven’t found anything that even touches on how to think about loops. Even worked examples, at least the ones I looked at, only show the solution then explain why it works, but not how the author came up with the solution in the first place.
Is there a resource I’m missing, or is this a need that has yet to be filled?