Identify the sub-skills

Paul Curzon, Queen Mary University of London

Here is the next in our series of tips about learning to learn (to program).

TIP 4: Know the sub-skills and practice them.

Paul Curzon, Queen Mary University of London

To learn a complex skill you need to practice, and so master, the right things a little at a time. That means you need to break it into sub-skills that you can work on separately. This is the idea behind deliberate practice too. What sub-skills have experts mastered? Practice those.

What are the sub-skills to practice to learn to program effectively? It is not just to write programs. For a novice there are a whole series of sub-skills that matter and that you can practice separately:

  • Being able to explain the basic programming concepts (like variables, loops, etc).
    • This matters as it is closely linked to the idea of mental representations of deliberate practice. This is building the basic mental representations.
    • Being able to compare and contrast related concepts is a useful extension to this.
  • Being able to read / trace simple programs and program fragments using each concept.
    • Reading a program doesn’t just mean read the words but being able to work out what the program does.
    • This helps ensure you have the right mental representations the skills are based on
  • Being able to think logically and focus on detail
    • This is actually a foundation to all the others
  • Being able to debug a program. There are two completely different skills here:
    • Being able to interpret compiler messages and correct compile time errors
    • Being able to find and correct run-time errors.
  • Being able to choose the right programming concept to do a task.
  • Being able to modify a program to do something slightly different, including
    • using the same constructs in a slightly different way, and
    • incorporating a new programming construct into an existing program so that it has a little more functionality
  • Being able to take simple problems and write simple programs that solve them using the appropriate concepts.

Bearing in mind the need to do things a little at a time, and the need to practice a lot, you should practice all these sub-skills on fairly small bits of code to start.

This does NOT mean that you shouldn’t be writing programs at all. It is in writing programs that you get the motivation to do the practice. Its also where you practice putting it all together. A tennis player still plays matches, but in between they practice serves, and hitting the ball to precise places, as well as general fitness, and so on. Similarly a programmer needs to both practice and write interesting programs.

It’s a bad idea to start on a large, complex programming project though, that may then be way beyond your current skill level. Instead master the skills for individual constructs and then build a small program gradually in to a larger project as you master each level of skill.

Once you have mastered the basic concepts and skills, then there is another level of skills to master. These are essentially what people talk about as computational thinking. There are lots of sub-skills involved (which is why academics argue a lot about what it actually is) but here is one list used:

  • algorithmic thinking
  • decomposition
  • generalisation including pattern matching
  • abstraction
  • evaluation

Much of this is applying advanced versions of the earlier set of skills, and you certainly do need to practice more complex versions of the original set including:

  • Reading real programs
  • Writing more complex programs

If you are a:

  • student
    • As a novice, practice all the above novice skills separately, focussing on one construct at a time.
    • Exercises to read/trace programs are especially important to practice.
  • teacher:
    • Set exercises that practice each of the skills separately, for each programming construct.
    • Exercises to read/trace programs are especially important to provide.

Mastering (programming) skills

Paul Curzon, Queen Mary University of London

Here is the next in our series of tips about learning to learn (to program).

TIP 3: Master skills a small step at a time

Working hard, putting in the time and effort is important … but it is NOT enough. You could jog every day for years and NOT get any faster or be able to run ever further. Similarly, you could spend hours trying to write programs but not get any better.  It isn’t just putting in the hours. You have to put in the right kind of hours.

You need to do the right kind of practice. The ideas behind deliberate practice suggest you should aim to master skills a small step at a time – but make sure you do master them before moving on.

(Sometimes you also need help to correct misunderstandings or just help you understand something critical. We will look in more detail at that in a later tip.)

Hours of jogging won’t improve your speed because you are not stretching yourself.  Switch to interval training where you run as fast as you can for a short distance and then recover for a short time, then do that again and again and again. Each session you are pushing yourself to your current limit… and you will improve. You are pushing your muscles and your brain just beyond what they can comfortably do at the moment. That’s the secret to improving.

Programming is the same. If you just spend hour after hour writing similar programs that you already know how to do, and never anything new, then again you won’t improve even if you do it for 10,000 hours. You need to practice what you are already capable of, but also push yourself to the things that you just can’t comfortably do (yet), programs you can’t comfortably write (yet). However this comes with a big BUT!

Don’t push too far beyond what you can do to quickly or you will just get hopelessly stuck and dispirited (the equivalent of collapsing on a run when you are a long way from home).

This is a mistake many students learning to program make. They are so keen to learn,  that having written a program that works, they rush straight on to the next concept. They are trying new things before they have actually mastered the current ones. You do need to write lots of programs with the simpler concepts to master them. If you move on too quickly you will start to struggle as you get more and more out of your depth, and the practice you do will be less and less effective.

There is no point trying to write programs that use while loops if you have not mastered the simpler if statements and so understand boolean expressions. And don’t move on to boolean expressions before you have mastered simple integer expressions and variables…

If you are told to try harder, don’t. Instead try smarter. That might mean stepping back to something simpler and make sure you have mastered it.

We will look in more detail what the right kind of practice is in future tips.

If you are a:

  • student
    • Practice lots, but master simple things before you move on to harder ones.
    • Do push just out of your comfort zone in your practice once you have mastered something.
    • Identify something just beyond what you can currently do and practice that until you can do it easily. Only then move on to the next thing. 
    • Don’t think that doing it once means you have mastered it!
  • teacher:
    • Set activities that gradually increase in difficulty with lots of exercises to practice at each stage.
    • Identify a progression in concepts to introduce.
    • Don’t let students rush on to writing ever more difficult programs to write before they have fully mastered the concepts in the previous ones.

More on Learning to Learn (to program)


IoC logo on white

 

 

“DELIBERATE” PRACTICE

Paul Curzon, Queen Mary University of London

Here is the next in our series of tips about learning to learn (to program).

TIP 2: THE RIGHT KIND OF PRACTICE IS “DELIBERATE” PRACTICE

10,000 hours of practice is often given as a rule of thumb to become an expert. However, if it is the wrong kind of practice then all those hours may not help. An effective kind of practice is “deliberate practice”. This is one of the top 10 or so educational approaches known to be effective as a way to improve learning, and is based on research in a lot of areas from elite sportspeople, virtuoso violinists, top medics and more. It is what elite people do to become one of the elite. It can help with learning to program too.

Some skills are more appropriate to apply deliberate practice to than others. However, even an incomplete version may make practice more effective.

Deliberate practice involves:

  • Identify the sub-skills that experts have; the ones that make them better than everyone else and practice those skills.
  • Aim to build accurate versions of the same “mental models” as the experts have for those sub-skills (early).

  • Practice doing things just beyond your current comfort zone until you have mastered them. Then move on.
  • Practice in a way that gives you immediate feedback on whether you are doing it right or wrong.
  • If you get it wrong (that’s good, its a chance to improve), focus on why you got it wrong (based on that accurate mental model) and practice until you can do it.

We will look at the separate elements of this in subsequent tips.

To learn to program you need not just to write program, but to do deliberate practice of the sub-skills.

If you are a:

  • student
    • Learn about deliberate practice and put it into practice when you learn, including as you learn to program.
  • teacher:
    • Set up learning resources and situations to support deliberate practice of programming skills.

Further reading: Ericsson, A & Pool, R, 2016, Peak: Secrets from the new science of expertise. The Bodley Head.


More on Learning to Learn (to program)


IoC logo on white

Learning to learn: Anyone can learn to …(program)

Paul Curzon, Queen Mary University of London

Tip 1: It is about effort not talent

So you want to learn (to program). The first and most important thing to realise is that with enough of the right kind of practice anyone can improve (and keep improving) at any skill (including programming). Our brains are amazing, they can just keep getting better if we do the right things.

Learning to program is not about talent. It is about effort and practicing enough in the right way.

There has been lots of research across lots of skills whether chess, violin playing, football, maths, english, swimming, memorising lots of random numbers… and the same result is found. Improving is not about innate talent. We all have wonderful brains. Genetics doesn’t stop anyone learning to be better. You can learn to program and you can get better and better.

Understanding this and putting it in to practice is called having a growth mind-set. Once you truly believe this and shake off the idea that some people are more talented than you, you have the keys to a new world of learning.

There is a rough rule. If you put in 10,000 hours of the right kind of practice then you too will be an expert – a wizard programmer. That is a lot, but then do that and people will call you a genius. Take it a step at a time. Practice for 10 hours and you will get better. Put in 10 more hours and you can improve more. Then put in 10 more and you can improve again. And so on. Put in 100 hours and you can be noticeably better. Then put in 100 more.

Shaking off the idea that other people are more talented is hard, especially when the person next to you seems to find it so much easier. Ignore them. They may have started before you; or done something previously that gave them a head start; or done more practice that you don’t know about, or have been practicing learning for longer, or … Don’t let them put you off. What they can do has no bearing on what you can do.

Also if other people tell you you have no talent, don’t let it get to you. They are just ignorant.  One day you can be great at it if you want to be badly enough and practice in the right way.

As a teacher, instilling a growth mind set in those you teach can help them do better too. Believe in every last student and research has shown they will do better, and improve more than if you don’t.

Learning is a skill to – so you can get better at it too with practice.

However, while working hard, putting in the time and effort matters, it is not enough. You need the right kind of practice. We will talk more about that in later tips.

If you are a:

  • student
    • Get in to the habit of whenever you find yourself saying “I cant …” or “I don’t understand…”, add on the end “…yet”. Practice doing it…
    • Constantly remind yourself that with practice you can get better, however hard it is now.
  • teacher:
    • Every time a student says “I cant …” or “I don’t understand…”, add on the end “…yet”.
    • Constantly remind your students: “It is not about talent. Anyone can learn to program. You can get better with practice.”

Further reading: Dweck, C, 2006, Mindset: How you can fulfil your potential: Constable & Robinson Ltd London


Watch out for more blog tips or …

get ahead with more on Learning to Learn (to program)


IoC logo on white

Learning to learn (to program)

Paul Curzon, Queen Mary University of London

We all learn naturally but some ways of learning are more effective than others. Whether it is a skill or knowledge you want to learn there are good ways and bad ways. This blog series is about how to learn effectively.

Programming is a skill based on knowledge. Everyone in the digital age needs to learn at least a little of how to program. (If you want to you can learn a lot.) We will therefore use it as a source of examples, but the tips here are also about learning in general.

Learn to learn and you can be more effective at anything you put your mind to.

If you are a:

  • student: learn more effectively
  • teacher: help ensure you get the best of your students

Shaping poems: Generalisation with methods

by Paul Curzon, Queen Mary University of London

Methods are an important way that computer scientists practice generalisation when programming. Whether you call them methods, routines, functions or procedures, they give a way to write generalised solutions to a problem rather than a one of solution that is no good for any other problem. I recently wrote a little method that was really just for fun, but illustrates some of the main ideas. It is all about printing a poem in a shape that matches a number sequence…

A Fibonacci Poem

BrianBilstonFibonacciPoem

Twitter recently doubled the number of characters allowed in tweets to 280 characters. Some think that this destroys the whole charm of tweets and the way they force conciseness. Brian Bilston tweeted this poem as a response which increases the number of words in each line according to a fibonacci number sequence. It is a famous sequence found often in nature such as in the numbers of petals on flowers and in bee breeding.  (See this cs4fn article for more) The sequence runs 1,1,2,3,5,8,13,21, … with each number obtained by adding the previous two. Brian’s poem made me think that shaping poems is a nice way to explore number sequences, so I started to play with a program.

 

I ended up with a really general program that could take different poems and different number sequences, but to start with I just tried to write a program that could simply print out Brian’s poem with line breaks following the fibonacci  sequence as he intended it to be formatted. Here is what I cam up with (using Python)…

First I needed to write a method that given a number n returns the nth fibonacci number. This is really easy using recursion as what you write corresponds closely to the way we described what it does in English. If n is 1 or 2 then the answer is 1, but otherwise it is the sum of the previous two fibonacci numbers. It works out those previous two fibonacci numbers using recursion – just calling the method fibonacci itself but with smaller numbers, so earlier positions in the sequence.

def fibonacci(n):
    if n == 1 :
        return 1
    elif n == 2 :
        return 1
    else :
        return (fibonacci(n-1) + fibonacci(n-2))

I can then write a method that calls that method and has the poem built in. The details of how this works don’t matter so much here as I’m focussing on generalisation – so ignore the detail if you wish.

Essentially what it does though is take the poem, and split it into a list of separate words. It then goes round a loop printing a line at a time. To do that it calculates the fibonacci number (I’ve made that bit bold below so you can see) for the line number we are up to and splits off that many words from the front of the list, joins them back in to a string and prints them. It then increases the counter that is keeping track of which words have been used, so that it is now pointing beyond those words. It then goes round the loop again to print the next line and the next …

def printpoem():
    poemtoprint = """I wrote a poem in a tweet but then each line grew to the word sum of the previous two until I began to worry  about all these words coming with such frequency because as you can see, it can be easy to run out of space when a poem gets all Fibonacci sequency"""
    splitPoem = poemtoprint.split(" ")
    line = 1
    startword = 0
    endword = 0
    
    while endword < len(splitPoem):
        currentlinelength = fibonacci(line)
        endword = endword + currentlinelength
        thisline = splitPoem[startword:endword]
        thislinetoprint = ‘ '.join(thisline)
        print(thislinetoprint)
        line = line + 1
        startword = startword + currentlinelength

    print()

All this method can do is print that one poem, as the poem is built in to it. We call the method

printpoem()

and that one poem gets formatted the right way… It can’t do any other poem.

I
wrote
a poem
in a tweet
but then each line grew
to the word sum of the previous two
until I began to worry about all these words coming with such frequency
because as you can see, it can be easy to run out of space when a poem gets all Fibonacci sequency

Generalise over data

That’s fine but its a bit restrictive. I’d quite like to be able to write my own poems and turn them in to fibonacci poems. I don’t want to have to write a method for every poem I write. We need to generalise the method to work for any poem we give it. Most programming languages provide a way to do this. You provide the data they need to do their job in the form of arguments to the method. You pass the data to the method and it stores it in its own local variable, but otherwise does exactly as it did before. For my method to format poems, the data is the poem. I generalise the method over all poems, by making the poem an argument. I get rid of the assignment to the variable poemtoprint at the start and instead put the variable poemtoprint in the brackets of the header line of the method to show it is an argument. It will be given a new value each time the program calls the method.

def printpoem(poemtoprint):
    splitPoem = poemtoprint.split(" ")
    line = 1
    startword = 0
    endword = 0
    
    while endword < len(splitPoem):
        currentlinelength = fibonacci(line)
        endword = endword + currentlinelength
        thisline = splitPoem[startword:endword]
        thislinetoprint = ‘ '.join(thisline)
        print(thislinetoprint)
        line = line + 1
        startword = startword + currentlinelength

    print()

 

With that small change, this new version of the method works for any poem to print. The poem is no longer hardwired in to the method. To get it to print the original poem, we provide the poem when we call the method by placing it in the brackets in the call as follows:

printpoem("""I wrote a poem in a tweet but then each line grew to the word sum of the previous two until I began to worry about all these words coming with such frequency because as you can see, it can be easy to run out of space when a poem gets all Fibonacci sequency""")

This prints the poem exactly as before. The change means, though, that if I write my own poem (ok, I know its not as good as the original) I can pass it to the method instead as follows:

printpoem("""My poem began well, completely under control. But it got carried away. Lines grew longer, how long I couldn’t say. They took on a life of their own, until I realised at last. They were following fibonacci’s sequence which meant that once they were expanding they just kept growing in length so, so fast.”"")

It gets formatted in the fibonacci way too – no new code needed!

My
poem
started off
completely under control.
But it got carried away.
Lines grew longer, how long I couldn’t say.
They took on a life of their own, until I realised at last.
They were following fibonacci’s sequence which meant that once they were expanding they just kept growing in length so, so fast.

The poem could be stored in a variable, then we just put the variable in the call eg

mypoem = """My poem began well, completely under control. But it got carried away. Lines grew longer, how long I couldn’t say. They took on a life of their own, until I realised at last. They were following fibonacci’s sequence which meant that once they were expanding they just kept growing in length so, so fast."""

printpoem(mypoem)

This gets the value of mypoem and passes it to printpoem.

Getting more advanced: Generalise over methods

At this point I start to get carried away. Why stop at Fibonacci poems. Why can’t we have Factorial poems, that get bigger really, really fast, or arithmetic ones, or triangular shaped poems, never mind, your common or garden rectangular shaped ones. Why should I write a new method for each! The only difference between any of them is the  number sequence. We need to generalise over the number sequence too.

In many modern languages, Python included, this isn’t a problem. You can generalise over methods. Methods themselves can be thought of as data and you can pass them as arguments to other methods. All I need to do is add another argument to the method that I will call numbersequence. It is just a variable, but one that can hold a method. The type of method it holds is one that, given a number representing a position in a sequence, returns the number found at that position in the sequence. We just need to replace the call to fibonacci that we had before with a call to whatever is stored in this variable:

def printpoem(poemtoprint, numbersequence):
    splitPoem = poemtoprint.split(" ")
    line = 1
    startword = 0
    endword = 0
    
    while endword < len(splitPoem):
        currentlinelength = numbersequence(line)
        endword = endword + currentlinelength
        thisline = splitPoem[startword:endword]
        thislinetoprint = ‘ '.join(thisline)
        print(thislinetoprint)
        line = line + 1
        startword = startword + currentlinelength

    print()

 

To call this method, you give it a poem such as Brian’s and a method that gives a number sequence and it formats the poem according to the method.

If we call it with Brian’s poem and our fibonacci function…

poem = """I wrote a poem in a tweet but then each line grew to the word sum of the previous two until I began to worry about all these words coming with such frequency because as you can see, it can be easy to run out of space when a poem gets all Fibonacci sequency”""

printpoem(poem, fibonacci)

We get his poem formatted as before. However, as we now have a generalised function to solve the problem, not a specific one, we can do lots more. We can pass it other functions defining different number sequences and so print it out in different ways. For example, if I want to make the poem’s lines just increase by one word at a time in a simple triangular shape, I just need a method that returns the length of each line. That is easy as all it requires is the nice simple number sequence 1,2,3,4, … It just returns the number it is passed, as the line number gives the required length of that line.

def righttriangle(n):
    return n

We can pass our general method my poem and this method.

printpoem(mypoem, righttriangle)

and the poem is automatically formatted a different way:

My
poem started
off completely under
control. But it got
carried away. Lines grew longer,
how long I couldn’t say. They
took on a life of their own,
until I realised at last. They were following
fibonacci’s sequence which meant that once they were expanding
they just kept growing in length so, so fast.

Of course we should really write poems that match the number sequence we are using so here is my first attempt. Writing the method was easy. The poem was a bit harder, but here goes…

trianglepoem = "I like triangles of all types, but square cornered ones are especially right for me."

printpoem(trianglepoem,righttriangle)

I get my formatted poem …

I
like triangles
of all types,
but square cornered ones
are especially right for me.

Getting REALLY advanced: Currying favour

We can now print out poems in any pattern we can write the number sequence for. The simplest shape of all, where all lines are the same length, raises a new problem, though, and to solve it we will need to be more sophisticated than ever in the way we think of methods. We need something called currying, which gives an amazing amount of flexibility.

Lets suppose we want to print out a poem in a rectangular way with 5 words per line. That is easy we just need the method that takes the position n and always returns 5 whatever line we are on.

def rectangle5 (n):
    return 5

However, we’ve just spent a lot of time praising the idea of generalisation so of course what we want really is to pass it the number of words in a line too. That’s easy. We just need two arguments.

def rectangleN (side, n):
    return side

The trouble is this doesn’t work with our poem-printing method. It expects a single argument. Remember, how it appears in the method is:

currentlinelength = numbersequence(line)

Ours has two arguments. It doesn’t fit. It has the wrong type.

What we need is a way to pass arguments to a method one at a time. That is called currying. You give a method one argument and it gives you back a new method that takes the next argument. Some languages give nice easy ways to do that. You can do it in Python, but it looks a bit convoluted as there is no nice syntax. In fact its doing exactly as described above. You define a method inside the method.

In our case we want a method that, when passed an argument that says the number of words you want to fix each line length as, gives back a method of the kind we need. It should return a method, rectangleN, that when passed a number always returns the same number – that fixed side.

def rectangle (words):
    def rectangleN(n):
        return words
    return rectangleN

So this method has a method defined in it, rectangleN, that is the specific method we want. Whatever value is passed as the first argument is the value the created method will return when called. The method as a whole returns the new method, rectangleN.

We then get the method to give to our printpoem method by calling this method with just one argument so, for example,

rectangle(5)

creates the specific method  we need to give the number sequence 5,5,5, …

We really need a poem about rectangles to try this out on, but my poem-writing skills are running  out of steam. So calling it on the original poem

printpoem(poem,rectangle(5))

gives us a more boring version of that poem (though at least this one fits on the page):

I wrote a poem in
a tweet but then each
line grew to the word
sum of the previous two
until I began to worry
about all these words coming
with such frequency because as
you can see, it can
be easy to run out
of space when a poem
gets all Fibonacci sequency

OK so it loses the point of the poem. We have at least solved the programming problem, though. Currying means we can now even pass generalised methods to a generalised method.

What’s the point of all this? Probably none, but I enjoyed myself writing poems and programs, so maybe you will. It’s possibly a fun way to explore number sequences by writing number-sequence programs with linked poems to visualise them. Or just take a favourite poem and visualise lots of number sequences using it. It does, however, show what generalisation in programs is all about  though. That aside we definitely should mix poems and computer science more. Roll on National Poetry Day