7
$\begingroup$

In my beginner experience as a teacher, the thing that's strucking me most is a sensation of my students' inability to express their (valid!) thoughts in code. Let me deepen it a bit.

I inherited a last year class that was supposed to know C from their previous years. Anyway they struggled in manually tracing the execution of the code they see, being it mine or theirs. After some work I could test and evaluate them again and it now seems to me that they are actually quite good at reasoning about a sort of "abstract idea" of what they want their algorithms to do: they can use our natural language to say things like scan this, sum that, stop here or there, and they are pretty much correct. What they seem to have big difficulties in is saying the same thing using a formal language.

They moreover have this (to me, crazy) habit of writing huge blocks in main() without splitting anything, and even worse the only way they think about of producing output is writing to the world: they don't contemplate returning from a function. One (brilliant!) boy once asked me with big amazement after my seemingly strong assertion: "oh, so you mean that strlen() is a function?". Of course I may understand where this comes from, but I suspect it's not healthy for their learning.

It seems to me that a possible reason of this observed behaviour might lie in having taught them to build huge state machines without making them have a clue that they are building huge state machines: they can reproduce some they have already seen but are really lost when needing to make a new one. Even having them apply small modifications seems to me more like a blind trial from their part than some reasoning about the meanings. The building blocks of the language do not play well together in their minds or they are just unable to think about the state transitions in the code.

If I have to push myself a bit more I can say that I suspect all this is a reflection of the non composability of state machines, making their use in teaching to absolute novices not effective, whereas functions are known to be composable and would presumably lead to better results, in terms of being a means for mapping an individual's thoughts to formal language.

Is my analysis plausible? Have you experienced anything similar? Is there any available scientific literature on these phenomena? How would you test them to investigate deeper their difficulties and how would you try to show them a more effective route? Is there anything you would do to improve the methodology for next classes?

Should state machines be introduced somehow more explicitly, with better examples, showing how do they actually solve problems before throwing them to the poor students with a burden of syntax that they know nothing about? They even told me clearly that syntax is the only thing they focused all their energies on in their previous years.

What I am doing in younger classes is proposing functions first (as in functional paradigm), to solidify the concept of building a small composable block that has input and output, waiting a bit more in the future to introduce state transitions using the classic drawing turtle of fame with the hope that its visual feedback might make things tangible.

$\endgroup$
  • 4
    $\begingroup$ I think answering their questions (and ensuring they feel comfortable asking) should probably be much higher priority in your teaching. If they're still learning, they probably need a lot of examples, too. I'm guessing they're not as fluent with C as your impression seems to be. They probably need more practice with it. $\endgroup$ – Shule Feb 22 at 23:01
  • $\begingroup$ I find your phrase "mapping an individual's thoughts to formal language" mildly disturbing. Just sayin'... $\endgroup$ – Scott Rowe Feb 23 at 21:40
  • 1
    $\begingroup$ state machines, as in finite automata? $\endgroup$ – kate Mar 7 at 4:48
  • $\begingroup$ Scott: writing code is giving a shape of code to one's thoughts of operations and the like, for disturbing it may be. $\endgroup$ – user9137 Mar 7 at 15:30
  • $\begingroup$ Kate: yes, state machines as in automata. $\endgroup$ – user9137 Mar 7 at 18:13
5
$\begingroup$

There is a lot to unpack here, but first, YES, the teaching methodology can be improved, but that is always true, an unending quest.

Next, I'll note that C is a language with enough pitfalls that many people struggle with it at the detail level, finding it harder to think more globally. My preference, perhaps not open to you, is to start with a language at a higher level of abstraction generally and then, later in the curriculum, work both downward toward the machine and upward from where you start. An OO or pure functional language, if well taught, makes abstraction easier.

However, the biggest recommendation I'd make to address the problems you state is to introduce (at the beginning) unit testing and test driven development (TDD). Find a good testing environment for your language and insist on its use: No Code Without a Failing Test.

I normally start out a course with TDD, combined with giving assignments in an already logically decomposed way. Instead of a large program to write (large is relative, of course), I give them the "top down" decomposition initially that, in C they would be able to implement by writing a lot of functions.

Thus, at the beginning, I relieve them of having to face a blank screen with a large problem and nothing but 'main()' to work with. Later you can continue this, but making the pieces bigger, requiring some decomposition (helper functions) be developed. My mantra for a "too big function" is about five lines. My mantra for "too complex function" is nesting more than two levels. You can break these rules for especially boring code, but not for the interesting logic of a program, or it will be impossible to understand and maintain.

Note that breaking a problem up into parts is how Agile Development (Extreme Programming, Scrum, ...) actually work, with the Customer (in this case you) giving the Team (your students) manageable problems each of which contributes value.

Also, with TDD, you can provide some tests initially along with simple requirements. Teach them to stop programming when the tests pass. Or, write additional tests and make them pass. But the tests are small and simple, usually without internal logic, and the application code needed to make them pass should also be simple function and uses of functions.

$\endgroup$
4
$\begingroup$

I am surprised to hear of State Machines being introduced anywhere early in a curriculum. They are a solution to a particular kind of problem, and wouldn't make much sense outside of it. So, this is an indication to me that the curriculum is getting things backwards.

Humans need to learn things starting with very concrete situations that are graspable. Then they can start to use those concrete ideas as building blocks. Then they can start to reason about the blocks, with a bit of abstraction, then a bit more. You can't start with abstraction, the brain simply does not work that way.

Eventually, after years of nights of good sleep, enough ideas and familiarity will accumulate for motivated people to start to think on their own about things like programming. (And math. Music. Art. Philosophy... Etc.) But it doesn't come easy or fast. Sounds like a song I heard long ago.

If people developed programming in a particular way, maybe that is the best way to teach people about programming? If brains produced those ideas in a particular order, maybe that is how brains work and we should respect that and follow it? You can't push a rope uphill, but pulling on it works every time.

$\endgroup$
  • $\begingroup$ "You gotta pay your dues if you wanna sing the blues, but you know it don't come easy. You don't have to shout or leap about, you can even play them easy." $\endgroup$ – Scott Rowe Feb 15 at 0:52
4
$\begingroup$

In addition to Buffy's excellent suggestions, I might suggest reframing your own thinking. You are thinking and speaking rather abstractly. Little phrases like "a means for mapping an individual's thoughts to formal language" and the question "Should state machines be introduced somehow more explicitly, with better examples" hint to me that you're really thinking about how to get them directly to your way of thinking.

The truth is that abstract ideas are not great starting points for great learning. Abstraction is a way of grouping seemingly disparate ideas to allow us to utilize them more readily or more flexibly. That's actually a key idea!

If we grok this notion of abstraction, then the way to achieve flexible, abstract thinking is to first have fluency with the smaller, disparate concrete ideas. Only after several of these ideas are firmly entrenched and comfortable can we then look at them as system and make successful links between the ideas. (BTW, I am using the word "successful" here as a marker for an abstraction that we can readily utilize.)

This fits in with our lived experience. Typically, we see some small systems, discover (or learn about) how they actually operate the same way, and then expand on the abstraction through new linked ideas over time. When the system works well, rich and complex ideas get boiled down to very simple underlying mechanisms, so that we ultimately can apply the abstraction readily in many different mental domains.

This distillation takes time, and it requires us to have at least a few well-understood pieces before it really kicks in.

Finite State Machines are wonderful, powerful concepts, but these students who haven't figured out when to utilize a function aren't there yet. Even if you show them the abstraction, they aren't mentally positioned to take any real advantage of it.

Therefore, your best bet is to hit the concrete ideas that you want hard. Make sure they have a few of the micro-skills, and provide direct instruction. Then, introduce abstractions slowly as systems are mastered.

One last thought for you: it's absolutely normal for kids to come out with only a middling understanding of the material in a prior course. As frustrating as that can be, we muddle by with half-understandings all the time in many domains. Don't think it beneath you to briefly run over material again, both to remind those who have forgotten, and to give a hand to those who never quite got it the first time. (Keep these kids in your heart when you make your reviews, and the kids who just needed a reminder will be better off for it as well.)

Good luck!

$\endgroup$
  • 1
    $\begingroup$ I always started to understand the previous math core course during the current one. $\endgroup$ – Scott Rowe Feb 20 at 0:24
2
$\begingroup$

I will add to this, choice of language is also important, learning C (and forget C++) and how to program at the same time is to hard (see zone of proximal development).

Even Java is to hard:

  • Explaining public static void Main(string[] args)
  • Explaining that all programs need a main, but don't put anything in it, except new Program().run(); (main does not scale).
  • Don't use static it does not scale, but we have to use it in lesson 1.
  • public/private in lesson 1.
  • void in lesson 1, but don't use it, we will use functions.

May be with Unit Testing we can avoid main, but this is just the start. As an experienced programmer, I can do Java, but I find it exhausting, the amount of extra work that I have to do, just to express the most simple of ideas.

So what to choose

A language with contracts: adding pre/post conditions, will give you a clearer idea of what it is you are trying to do. If you don't know what you are trying to do, then how can you do it.

A language with a clean syntax, no extraneous words (public static void …). = means =, and assignment has its own symbol/keyword.

Is functional, or promotes functional.

A language with good defaults: data-attributes are private by default, methods are public by default.

The programming model, maps well to the theoretical model. E.g. Theoretical Object-oriented model maps to programming model (this is not true of most OO languages), or functional model maps to programming model.

Has a simple exception system (these are often overly complex). Not to hard to understand, but hard to work with.

$\endgroup$
  • 1
    $\begingroup$ COBOL was successful for a reason, yes? $\endgroup$ – Scott Rowe Feb 20 at 0:26
  • $\begingroup$ @ScottRowe Yes, and the reason was, that is all we had. We have learnt a lot since then. There are much better languages, for both teaching and professional programmers. Your argument is like Microsoft is successful for a reason. In this case it is because they used to be cheaper than Unix. Now it is because we follow each other. (perfect market hypothesis, see jimabbondante.files.wordpress.com/2010/02/… and notmanywise.files.wordpress.com/2012/02/caterpillars2.jpg $\endgroup$ – ctrl-alt-delor Feb 20 at 9:32
  • $\begingroup$ We have better languages. People do not get better every generation though, they all start with no concepts or experience. The Tower of Babel was not successful. A one-story house is. $\endgroup$ – Scott Rowe Feb 20 at 12:53
  • $\begingroup$ @scottrowe sorry if I suggested that things are getting better. My impression is that there was a flurry of progress in the 1960s, and then not much else. (an exception being N vs NP, sat solvers. I hear there has been much progress in these over the last 20 years, but these are not programming languages (or for beginners). $\endgroup$ – ctrl-alt-delor Feb 21 at 9:23
  • $\begingroup$ The video "The Future of Programming" is just as true today as the day it was made. I think everyone should see it. But, I am about to show coworkers how to build web applications with ASP.Net and ADO.Net. $\endgroup$ – Scott Rowe Feb 23 at 19:35

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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