What are healthy, productive ways to encourage students to progress to more advanced constructs as opposed to staying with the familiar?
I would say that it's the educator's main goal to showcase the necessity of using these constructs.
In general (even beyond CS), you should only take the more complex option when you need to. This is the core essence of KISS.
Therefore, if you want your students to use a more complex alternative, you need to present them with use cases where the simpler option simply doesn't work (well).
For example, when they are taught arrays, they continue to use individual variables when they are able.
You've indirectly answered your own question: "when they are able". Give them an assignment where they are unable to use individual variables.
A very simple example is an application that builds an array of 20 names (supplied by the user, one at a time), after which the program generates a sorted comma-separated list of names.
I don't see a way to sort these names by using individual variables. Even if some (hugely contrived) approach exists, it will take more time and effort to develop compared to a simple array sort.
If some students use the arrays; the hardheaded others will see that their peers find the task much easier than they do, which will naturally incentivize to do the same.
After working extensively with arrays, they don’t want to move to using built-in collection types that offer more flexibility and behavior.
A very simple example is an application that builds a list of names (supplied by the user, one at a time) until the user says "stop", at which point the program generates a sorted comma-separated list of names.
I don't see a way to do easily solve this with an array. Since you can't know how many names will be supplied, you can't definitively declare your array (you'd have to constantly recast your array to a bigger size).
If they use a massive array to begin with, then they should be docked points for irresponsible memory management (for the same reason that you'd dock points for memory leaks).
While continually recasting the array is not impossible to do, it is contrived and it will take more time and effort to develop compared to using a list.
If some students use the lists; the hardheaded others will see that their peers find the task much easier than they do, which will naturally incentivize to do the same.
When introduced to the foreach loop, they continue to use bounded for loops.
The problem here is that every foreach
loop can be written as a for
loop. There is no real benefit to using a foreach
over a for
, the only benefit is the ease of reading/typing.
A similar example would be LINQ, which is mostly used as shorthand for a foreach loop. In essence, every (in-memory) LINQ query can be written as a manually developed for
loop. That's technically not wrong (it only affects readability and increases effort required; it doesn't functionally matter).
Currently, your students prefer to use a for
loop because it takes them less time to use something they know, compared to something they don't yet know (foreach
).
I would temporarily ban the use of the for
loop (or force them to use foreach
, same thing), to force your students to familiarize themselves with the foreach
. After a few (iteration heavy) assignments, they should be familiar with how to use a foreach
.
In the future, when they are familiar with both the for
and foreach
, it's up to them to decide what to use. Most likely, most will pick foreach
because it's easier and less prone to developer error.
But the traditionalists who revert to using for
loops are not really wrong (though they may be docked some points for readability, if you grade readability too).
It is sometimes difficult to impress upon beginning programmers the usefulness of “new” constructs, because the exercises and programs they are capable of undertaking do not sufficiently demonstrate the power and usefulness of those constructs.
This is a contradiction. If you don't think the students are capable of handling a problem of this complexity, then you shouldn't be trying to teach them the solution (to a problem that they can't grasp).
In other words, if you want to teach them something, of course you're going to have to show them a situation where it is relevant to use the solution!
foreach
before generalisedfor
. Foreach is easier, and will be used 80% of the time in there future programs, so teach it first. Same for container classes. This is related to should I start high or low. Well start as you mean to go on. Students will value and use what they learn first. $\endgroup$