53
$\begingroup$

In my experience, there are always students who are resistant to moving to more advanced constructs. They want to stay with what they already know. For example, when they are taught arrays, they continue to use individual variables when they are able. When introduced to the foreach loop, they continue to use bounded for loops. When they learn about functions, they resist modularizing their code. After working extensively with arrays, they don’t want to move to using built-in collection types that offer more flexibility and behavior.

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.

What are ways to encourage beginning programmers to embrace more advanced constructs without penalizing them? I am looking for more "carrot-y" answers than "stick-y".

$\endgroup$
5
  • 9
    $\begingroup$ Some of it is teaching order: Do foreach before generalised for. 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$ Apr 11, 2018 at 8:11
  • $\begingroup$ Have something with a good reason to use more advanced constructs. With respect to premature optimization it may actually be a reasonable idea to use a few variables instead of an array, so your current premise may be wrong. Not every neat data structure must be used and people have different programming styles. To teach them to use an array show them a task which cannot be (reasonably) solved by introducing more and more variables. $\endgroup$
    – allo
    Apr 12, 2018 at 8:56
  • $\begingroup$ People that progress from simple things at a slower pace, tend to practice the simple things more and better... $\endgroup$ Apr 14, 2018 at 21:23
  • $\begingroup$ Comments are not for extended discussion; this conversation has been moved to chat. $\endgroup$
    – thesecretmaster
    Apr 24, 2018 at 0:44
  • $\begingroup$ Two things. 1, you can't teach curiosity. 2, if the students aren't capable of grasping programs where the constructs you're trying to show them are actually useful, then perhaps you are teaching them to run before they can walk? $\endgroup$
    – Steve
    Apr 29, 2022 at 6:48

12 Answers 12

55
$\begingroup$

I'm afraid that there is no single silver bullet. The problem you've pointed to is very real, and isn't limited to students. All of us tend to stick to our own familiar toolsets because, well, it's easier. We know how to use them already.

Enter the Motivating Example. This mythical thing makes a new topic so useful that the new topic is practically a requirement. Want to motivate recursion? Try having kids determine the file-size of a folder (including its sub-folders). Want to motivate methods? Try giving a main method that calls them, and ask the kids to create the code that will make everything work.

Finding a great, motivating example feels like a search for hidden treasure; it is painful and slow. You're seeking something of great value, and it's hard to know where to look. To make matters worse, a good motivator for one teacher may not work for another, because what makes a motivating example work is intricately tied into both the broader curriculum and what sorts of things the teacher emphasizes within the classroom.

And once found, they are indeed treasures! Clean, motivating examples make the value of a topic immediately apparent to students. Resistance melts away, and is replaced by enthusiasm for the new tool that is now making their life easy.

Sometimes I spend weeks trying to find a good motivator. I've found that teacher communities (such as this very one!) can be great sources of inspiration for them, as can algorithms books. Or, as Kevin pointed out in a comment (before it was deleted):

As far as finding motivation is concerned, it may be helpful to look into the history of the construct. For example, foreach used to be called "the iterator pattern," and even appears in the GoF book under that name, before there even was a foreach loop. So foreach isn't about simplifying the code so much as it is about abstracting away the implementation details of "get the size of the container" and "get the nth element" into a single opaque accessor. That suggests that a motivating example might begin by introducing trees, showing traversal code, and then contrasting it with an array.

Even with all of these tools, there are no easy guarantees. I sometimes have to simply wait for my "Eureka!" moment, and teach the best I can in the meantime.

$\endgroup$
0
26
$\begingroup$

I'm not sure the following will work for all students, but I remember this being a transformative "aha" moment in my own education:

Show them your code.

Preferably contrasted against a functioning but god-awful student submission from the previous term. Show them how its an order of magnitude simpler than their shotgun-formatted needlessly complex mess. Show them what compactness and elegance look like. In freshman?sophmore? CS prof assigned us to write a CLI memory card game. I like many of my peers did this big elaborate class hierarchy and/or held the "cards" in nested data structures with nested loops. The prof's code was IIRC < 20 LoC: he used a flat array of structs with the modulus operator to determine the "row". I learned that day what Torvalds meant by "taste", namely the ability to know when to use "industry best practices" and when to just write simple code even if it's less flexible.

I realize that's almost the opposite point than the one you're driving at (i.e. getting them to use the more advanced constructs) but show in your code that they exist for a reason. If you can replace 50 lines of tedious, error-prone buffer manipulation with a built-in collection and an iterator/forEach that takes less than 10, believe me the smarter ones will notice. And when they collaborate with their peers on group projects peer pressure will help with the slower ones.

$\endgroup$
1
  • 1
    $\begingroup$ Time pressure probably helps even more than peer pressure. ;) $\endgroup$
    – jpmc26
    Apr 11, 2018 at 23:32
16
$\begingroup$

There are a number of facets to this question. But the first thing you need to remember is that students (like anyone) will tend to apply solutions that they are most familiar with. In the student context that means, often enough, the things they learned earliest and have been practicing with the most. This effect is most pronounced on hard problems that may not have an obvious (to the beginner) answer with the new techniques.

For the above reason, I prefer to teach a thoroughly object-oriented course to students, having them build classes and write and use methods (some of mine, some of theirs) before they seen things like primitive data (int, double...) or low-level program structure (if-while...)

More generally, I advice against a strictly bottom up approach to teaching programming, in which low level constructs are taught first with a lot of emphasis on language primitives. It takes quite a lot of scaffolding, however, but my students start their programming learning in a world that I construct, not a "naked" language. So, when they come up with a hard problem and need to "fall back" on what they know best, it will be something like writing a test for something or an interface for structure, rather than just trying ad-hoc methods.

The reason for this is that ultimately I want to teach them to be OO programmers and when they get a new problem, I want them to think in a thoroughly OO way. So they will naturally write 3 line methods rather than 30 (or 300) liners. If I try to teach them procedural programming first, that will be their fall-back and I want to spare them that pain.

However, if you don't want to employ such an "extreme" change in the course, there are other things you can do.

One method is to employ a Spiral approach in which a lot of topics are introduced early with only relatively shallow usage in the first loop of the spiral and then repeatedly going deeper on each while you introduce new topics also. This, rather than completely explaining all the nuances of any given topic. (Spiral Pedagogical Pattern)

Another trick you can use, whatever your overall syllabus, is to introduce a rather large program early on that shows a fair number of important topics. One way to do this is to give them a program that is essentially correct, but which you have "broken" in a few ways. Their assignment is to understand it enough to fix it up (Fixer Upper Pedagogical Pattern)

Ultimately, then, the answer isn't really to "encourage" them, but to structure the course itself so that the "higher level" topics come before the lower level ones.

There is really no reason why arrays need to be discussed before (Java) collections/lists/maps... Then the foreach construct becomes their fallback and you can later teach them the c-like $for$ loop as a possible way that foreach can be implemented. I'd rather they used the libraries in their code in any case, just as I want them to program within my original framework rather than riding bareback on the language.

If you introduce "deep" and "important" topics only late in the course then it is with those that they will have the least practice and experience.

tl;dr: rearrange the course so that they get the most practice on the most important topics.

$\endgroup$
4
  • $\begingroup$ "The reason for this is that ultimately I want to teach them to be OO programmers and when they get a new problem, I want them to think in a thoroughly OO way" - isn't FRP the hot new paradigm nowadays? $\endgroup$
    – user253751
    Apr 10, 2018 at 23:40
  • 1
    $\begingroup$ This! Why is the OP teaching bounded for before foreach? $\endgroup$ Apr 11, 2018 at 13:43
  • 2
    $\begingroup$ What you said is true if you want to build software devs or web dev that knows just enough for what their aim is to, but that doesn't include embedded dev for instance. If you don't understand the low level approach, I don't feel like people should use more advanced features. Or if they do use those collection they have to code them in the first place to understand how they work. $\endgroup$
    – YCN-
    Apr 12, 2018 at 10:06
  • 1
    $\begingroup$ @YCN-: I personally love the low level stuff, but I think this advice sounds reasonable. Getting to the point of understand how different collections work (like sorted maps vs. plain hashes vs. simple std::vector) requires some algorithm knowledge, and understanding efficiency is only truly possible with some computer architecture (caches) and assembly language (and how source compiles to asm). Leaving these topics for later in the spiral makes sense: by the time you get to the implementation details and low level stuff, students will be ready to understand it better. $\endgroup$ Apr 15, 2018 at 0:22
14
$\begingroup$

Rather than trying to motivate the students, or the reluctant ones, to progress into using your new tool (foreach after for), and finding a new motivating example every time, try to motivate them to be self-interested, lazy coders.

Now that I'm under the tar and feathers, I'll explain. People like to find the easiest way, not the best way. Earlier postings reveal that you're in the teen-age world with your students, and they are no exception, if not the rule, to that thinking. Knowing that your students will have that natural preference for easy, use it as the motivation. "Quality is job one" makes a good slogan for a company, but isn't so good for motivation of students.

Borrowing from the world of sales, show the problem first, and the solution second. To progress from a bounded for towards a foreach, as an example, use the bounded version in a manner already natural to them that is possible, but problematic, against a collection that can be foreach-ed. Legitimately work out the checks and error traps and actually make it work. Once they've experience doing it that way, then you can present the "package" where someone did all that work, and more, when they created the foreach. All the traps, special conditions, etc., are handled, and all the work has been done, and debugged, for them.

The same approach can be used to step-up to any progression you choose. Under the hood in any chosen language the new tool might not be implemented using the old one. At least not now. Still, that's how they came to be created in the first place - refinement on a common problem and its solution creates the shiny new practice which becomes a tool. That's how we got structured programming to begin with.

The key is that the new concept is not presented as better programming, better presentation, or even easier to maintain, update, or fix later. Rather it's presented as a real solution, to a real problem, right now that makes less work right now for them. Less time on coding, now, means more time, now, to be with their friends, or do other fun stuff. After a couple rounds of that, once the light bulb goes on, they'll start actively looking for pre-baked solutions, and libraries will be their next target. That outlook will carry them well into the future, and could even encourage them to look at new languages, either new to them, or new to the world, for an easy solution to a problem.

Hence, they become self-interested, lazy coders.

$\endgroup$
1
  • 3
    $\begingroup$ I volunteer teaching 12-14 y/os and this a running theme: Yes, you can make a giant if/elif/elif/.../else block... but I'm lazy, so here is a much faster way to do it. By casting it in that way (and ensuring they are comfortable with using it), they tend to move over pretty quick. $\endgroup$ Apr 12, 2018 at 22:50
11
$\begingroup$

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!

$\endgroup$
1
  • $\begingroup$ I don't see a way to sort these names by using individual variables. Hard-coded sorting network where every compare-and-swap is specified separately (hopefully with the compare+swap wrapping in a function call). A sorting network for 17 elements can be only 71 comparators according to en.wikipedia.org/wiki/Sorting_network, so 20 elements is at least plausible to write out in the source code, and I think there are sites online that can generate them. Anyway, totally implausible for beginners to actually come up with, but in other contexts it's not totally unreasonable. $\endgroup$ Apr 15, 2018 at 0:32
9
$\begingroup$

As a student, I can say the appeal to my free time is pretty powerful motivation. I have spent hours implementing features already present in a language only to feel foolish when I realized my folly.

For example I had an assignment where I spent a good chunk of time implementing my own (very flawed) version of a HashMap. I have some the same thing for a TreeSet as well.

Many of my peers will cling to an ArrayList no matter what the problem presents. They are just comfortable with using it and prefer it over any other structure, even when that means a lot of extra code they have to write.

Elegant solutions are appealing to some students due to their brevity, which can be seen as clever or almost getting away with something. That may motivate some, especially the lazy ones. Others may just appreciate how elegant the solution is for it's open merit.

I think some students will stick with Array and ArrayList until they are basically forced to not use them. No matter how clever or elegant they could potentially be. Fostering a culture that embraces failure may help overcome that.

I have found online interview prep sites have done the best at nudging me to use constructs I would have otherwise ignored. Maybe sell them on the idea of interview prep? Can't use an Array for all of those, just flat won't work and that is a time you definitely want something to work!

$\endgroup$
0
4
$\begingroup$

If you start with the most absolutely bone-simple programming concepts at the start, then as you introduce each more complex concept, it is obvious why it solves a problem that just could not be solved using the simpler stuff they already know: No number of separate variables will solve a problem that calls for arrays. No fixed size array will solve a problem that calls for expandable arrays, and so on.

Begin at the beginning, with things that could not be simpler or more obvious. Add one thing at a time, and show how it solves problems. Steam engine, gasoline engine, gas turbine, rocket. You just will never get to space with Jules Verne's coal-powered "rocket train".

If someone wants to stick to older concepts that won't solve a new problem, they plain old won't solve the new problem. They can't stick, they either go forward with either new concept, or they fail out. This is why we reproduce the development of programming as it occurred: no one ever gets lost trudging along in the well-worn path. They never ask why something is better than something else: they know.

$\endgroup$
0
2
$\begingroup$

Challenge them with something they'll have to do once they're working -- give them some code that uses the technique you want to teach them, and have them make a change of some sort. Or show them code that DOESN'T use the technique, require some change to be made to it, with requirements (complexity, line count, whatever) that drive them towards the technique you want them to use.

$\endgroup$
0
1
$\begingroup$

When I used to be a student learning C++, my teacher gave us a very open subject with only a subject : Star Wars (it was for the year of the release of the new series). The only consign was to use at least several C++ functions. We had a minimal list of the functions and structure we had to use, and could add any specific function we would have liked.

We had two different delivery of the project, first one was for the teacher to point out some place where we could have coded better (using more C++ built in function) and the second was for the final delivery with proper coding.

Honestly this was a very challenging way of giving us a project, every body get deeply into it and we ended up with very pretty and satisfactory programs.

I guess this kind of approach is very appreciated but the teacher has to give a huge amount of time in order for that approach to succeed.

$\endgroup$
0
1
$\begingroup$

The persons who could answer your question best are the students themselves. Why don't you ask them? Seriously though, there might be a host of reasons why a given person is novelty-shy.

First of all, however pleasant knowledge might be, any learning process is intrinsically painful, so some reluctance is only to be expected from any pupil or student at some point. Besides, computing is extremely abstract. Maybe not as much as maths, but still it takes a bit of time before you can really create something beyond learning basic vocabulary. For those who are passionate about the subject it's a breeze. For others, that initial period requires a leap of faith.

Maybe some of your students are simply not interested in programming, in which case they will likely try and avoid the chore of learning more unpleasant stuff.
Or maybe they are struggling to keep up and feel too insecure to tackle new concepts.
Or maybe they are having it too easy and don't see the point in learning new tricks if the problems they are given can be solved without.

Depending on the cause of their reluctance, you might for instance:

  • openly discuss their lack of interest and make a deal with them: do the bare minimum honestly and I'll try to make it as painless as possible
  • work on the student's self confidence. Try to find a couple of relays among the most advanced students that can spend some time helping others in group projects.
  • give them something worth tackling the new concepts. Ideally a project where old tricks either don't work or are too impractical.

As a student, my best memories are from classes where I felt I belonged. Not necessarily as a successful student, sometimes quite the contrary. By "belong" I mean I knew what to expect and felt in agreement with the teacher - even if that was sometimes limited to agreeing to disagree :)
I loathed electronics and had disastrous grades, but I managed to strike a deal with a few teachers who would clearly state what they expected of me and respect my lack of interest. I also had a couple of teachers who took the time to devise problems for advanced students and kept us interested while the rest of the class struggled with system programming basics. Plus a couple of teachers who helped me overcome an initial reluctance to dive into some domains. Lastly, I remember one college teacher to whom I am still grateful today for opening a window to all that marvellous literature. Decades later, I met again with a college friend, and to my consternation she told me these French courses had bored her silly :). It took me a real effort to accept the idea that these lectures I had found so captivating could have been perceived as plain chores by some! So any teacher's trick is likely to work magnificently with one student and fail spectacularly with another.

I tried to emulate that behaviour in my few teaching experiments, with mixed success to be honest, but at least I always avoided the pitfall of being a one trick pony.

Always try and listen to what the students have to say (if anything), that would be my first and last advice.
If they know you sincerely try to take their needs into account and expect reasonable things of them - within the limits of what you are expected to teach them, of course, they will usually go to great lengths to be worthy of your respect and fulfil your expectations. At which point problems like the one you evoke in your question cease to be major issues, in my limited experience.

$\endgroup$
0
1
$\begingroup$
  1. As mentioned in a comment: start with high level constructs when possible.
  2. In some assignments I give students the main program, and then tell them, write the data structures and functions that make this work. That way you're forcing them to use the high level constructs, and maybe they'll see that the shorter/more elegant expression is preferable.
$\endgroup$
0
1
$\begingroup$

I have spent a lot of time on chatrooms/forums helping people with their programs, and over time what I've found is:

  • Provide partially written code to help them feel comfortable with it. Be sure the code you give is clear and concise. It needs to focus on the point and avoid being more confusing than it must be. (I find this helpful for people asking for help.)

  • Give solid reasons for why they should use something. If you just tell them "this does X" without explaining why they would want something like "X", then they'll never know when they should use it over something else. If you're teaching these advanced concepts, there should be a reason you're teaching them.

  • Make sure they try it. For many, code snippets during lectures are nothing more than code snippets. People tend to have more attachment to their own code because they understand their own code more than a stranger's. This means they aren't really understanding why the code you're showing them actually matters to them.

Now, that's cool and all, but how should you actually integrate this advice into a classroom setting? I'm not a teacher, but here's some ideas:

  • Provide partially written code alongside assignments as hints. Whether or not a student uses it is up to them, but it'll encourage them to think about stuff like "where should I use this?" and "how should I fill this in?".

  • Make it obvious how the given reasons for using something over something else would apply. If you're going over lists vs sets, then even have them implement something simple using both ways so that they can see the difference first-hand.

  • Give them a reason to look back on their own code. For example, have them do peer reviews after they finish the assignment. Most students won't look at their code after they've turned it in, so they don't learn from their own mistakes.

$\endgroup$
1
  • 1
    $\begingroup$ I like your opening point. Clear and Concise. I encounter a lot of examples where the point is buried is huge examples, or hidden behind obfuscated code. Python is particularly horrendous when it comes to hunting down examples as everyone goes off in their own confusing direction. $\endgroup$
    – codingCat
    May 12, 2022 at 13:30

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.