Let me ask you a question:
What does a programmer do all day?
Stare at a monitor and type? Attend meetings? (Ha!). Read Hacker News? (Double ha!)
Seriously, mostly what they do is think, which is why they’re considered knowledge workers.
Sounds cushy right?
Sitting around thinking all day. It’s sure better than cleaning fish or hauling ice (I recommend neither), but it’s no walk in the park.
Programmers are held accountable for the quality of their thinking and the decisions they make based upon it.
Poor thinking means poor code. Poor code means software bugs, projects running over time and over budget, and everyone working late to fix bad software.
You may be thinking at this point – well bigger brains mean better software, right? If there’s more IQ to pour into a problem we’re going to get a better solution.
While having a few brain cells definitely helps, the IQ advantage goes out the window when we’re stressed, sleep-deprived, angry, in love(!) or generally just being human.
What saves us from ourselves? Tools and techniques that allow us to reduce complex, overwhelming problems to small, achievable tasks.
These techniques comprise computational thinking.
Computational thinking doesn’t mean thinking like a computer – just the opposite! It’s where you use your ability to think like a human to solve problems with a computer.
In today’s article I’ll give you a framework for thinking about problems that you can use to develop great solutions.
Here’s what we’ll look at and practice today:
Sound weird? Don’t worry, it won’t by the end of the article.
By the end of this article you’ll have the tools to:
Sound too good to be true? I didn’t say it was going to be easy!
But first, you’ll have to meet your new robot!
Congratulations! You’re the proud new owner of a brand new UrkelBot5000. Just like you always wanted.
He’s a domestic robot, and besides bursting into your kitchen at odd times and making whiny noises, he’ll understand and DO anything you tell him – anything at all!
The only problem is, he can’t really think for himself.
So before you can enjoy your new domestic helper, you’ll have to train him.
Let’s start with a nice hot cup of tea.
This is actually a very controversial question – people from the UK, India and Japan will have very different ideas about it. Never mind the rest of us.
But we’ll keep things simple for UrkelBot.
We’re going to ask him to make tea by adding a tea bag to a cup of freshly boiled water (from an electric kettle).
So before we go any further, please write down the steps for making a cup of tea (make sure you don’t leave anything out!)
So, instead of me trying to read your mind and guess how you make tea, I’ll give you the list I made sitting in an early programming class:
1. Boil water in kettle 2. Put teabag in cup 3. Put boiled water in cup.
That works with humans because we have a similar frame of reference. We’re all more or less familiar with cups, tea and kettles.
UrkelBot however, is a machine, and doesn’t share that frame of reference. So we really need to spell things out for him.
This is where we start decomposing our problem – i.e. break it down into smaller problems.
As you can see, young Luke has already done this somewhat. Our ‘make tea’ problem has been turned into three problems. But is this enough? Let’s consider the first step:
“Boil water in kettle” is fine for you and me, but UrkelBot? He’s not too sure where this water is coming from, nor how to make it ‘boil’.
So maybe we can break this problem into:
1. Fill kettle from tap 2. Push button on kettle to boil water.
So that’s a little better, but did UrkelBot just splash water over the top of a closed kettle? Did he produce any water at all?
So “Fill kettle from tap” needs to be spelled out too:
1. Grasp kettle by outer handle. 2. Push button on the top of the handle to open the lid on top of the kettle. 3. Place open top of kettle beneath cold-water tap. 4. Open cold-water faucet (I’ll leave the choice between turning the faucet anti-clockwise vs raising the lever up to you and your kitchen). 5. Close the cold-water faucet when the water level in the kettle reaches the MAX indicator on the kettle. 6. Close the lid on the kettle.
And UrkelBot might have a bit more luck filling the kettle with those instructions.
Too bad we’re only one third of the way to a cup of tea.
If you went through this process already and produced a nice, take-nothing-for-granted list of instructions for making tea – then congratulations! You’re a natural at decomposition!
Otherwise, now might be a good time to re-examine your steps for making tea, and expand them to the point where UrkelBot can do no wrong.
A program is only as good as the assumptions it was built upon, and the same goes when we teach tea-making to UrkelBot.
The thing about this exercise is that we’re making tea. I’ve made an assumption (an explicit one) that you drink tea, and not coffee or some other hot beverage. This isn’t really a reasonable assumption (I prefer coffee, myself), but at least it’s been stated.
Hidden assumptions are trickier. These are things that are so obvious that we don’t even think about them.
I’ve been talking all this time about putting teabags into a cup, but do we actually have any tea in the house? Does the cup need washing? Is the kettle plugged in (and the electricity bill paid)? Was the kettle full already? And so on.
If you were actually standing in a kitchen these things might have jumped out to you as obvious.
But since we’re dealing with an imaginary kitchen they may be a little more difficult to anticipate.
That might not seem fair, but that’s the situation we’re often dealing with when we’re programming – imagining data, files, network connections and so on. Many things you’ll never see in the flesh.
So we need a tool to help us uncover these assumptions.
One thing I like to do is list all the states of the things I’m dealing with, for instance:
And so on. This helps make our imaginary kettle a little more concrete.
Examining each of its states in turn can highlight problems in our solutions.
Again, it’s a way of decreasing the amount of complexity our brain must deal with in a single moment. (You might see a pattern forming here).
So try it yourself. List the states of the Tea, and the Faucet, and then compare them to the steps you’ve developed so far.
You’ll hear a lot about algorithms in the programming world. College courses have multiple semesters devoted to them and there are hundreds of books on algorithms.
But we can define an algorithm simply as a series of steps that lead to a solution.
So we started working on an algorithm for making tea during our problem decomposition.
You’ll notice that our definition of algorithm doesn’t say ‘maybe leads to a solution’ or ‘sometimes leads to a solution.’ An algorithm is no good if it doesn’t get us to a solution.
Luckily we fleshed our first steps for UrkelBot’s tea-making out by examining assumptions. When we incorporate those into our tea recipe, it gets a little more robust.
But now it’s time to express the tea recipe as an algorithm a little more formally.
First, we need to know that an algorithm is made up of three types of things:
We’ll apply these to our ‘Fill the kettle’ algorithm, and incorporate extra steps to deal with some assumptions identified earlier.
Since we already have a series of steps to execute, we’ll move on to decision:
1. If the water in the kettle reaches the MAX indicator; <strong>Then</strong> a. Finish here (there’s nothing to do!) 2. Grasp kettle by outer handle. 3. If the kettle lid is closed; Then a. Push button on the top of the handle to open the lid on top of the kettle. 4. Place open top of kettle beneath cold-water tap. 5. If the cold-water faucet isn’t open; Then a. Open cold-water faucet. 6. Close the cold-water faucet when the water level in the kettle reaches the MAX indicator on the kettle. 7. Close the lid on the kettle.
This is also called branching, because the code branches into different paths.
The If…Then construct is an extremely common way of implementing decisions in programming languages. If you’ve done even a little programming before you’re bound to have run into them.
You’ll notice that they allow us to skip steps that don’t need to be performed, and avoid making mistakes.
What’s more, this is the kind of instruction UrkelBot understands: If this, then do that… And our tea recipe is looking a little more like a program.
Because that’s what a program is – an algorithm encoded in a form a machine can understand.
Let’s move on to repetition, more formally known as iteration. Less formally known as looping.
Why looping? Because sometimes in an algorithm we need to loop back to an earlier part of the algorithm and repeat some steps.
Let’s add it to the kettle-filling algorithm:
1. If the water in the kettle reaches the MAX indicator; Then a. Finish here (there’s nothing to do!) 2. Grasp kettle by outer handle. 3. If the kettle lid is closed; Then a. Push button on the top of the handle to open the lid on top of the kettle. 4. Place open top of kettle beneath cold-water tap. 5. If the cold-water faucet isn’t open; Then a. Open cold-water faucet. 6. Has the water reached the MAX indicator on the kettle? a. No? i. Wait 1 second ii. Go to step 6 b. Yes? i. Continue to step 7 7. Close the cold-water faucet. 8. Close the lid on the kettle.
Now we repeatedly wait until the kettle is full. Once again, repeating code like this is often referred to as ‘iteration’ or ‘looping’.
The appearance of ‘Go to’ may give some people the shivers here, because ‘Go to’ commands (called ‘goto’) were once abused terribly in early programming. Goto was synonymous with messy, undisciplined code.
But don’t worry! All programming languages (and programmers) use ‘go to’ style statements all the time, it’s just that they’re dressed up with a different name that forces us to use them well. These are ‘looping’ constructs, often called ‘for’ or ‘while’. We’ll look at using them in a later article.
Finally, we should ask ourselves “is this a good algorithm?” Is it going to give us a full kettle every time? I think if we state some outstanding assumptions (‘water is available’, ‘kettle lid works reliably’, ‘the faucet works’) then we’ve got a good algorithm.
Over to you – try applying these constructs (execution, decision and repetition) to the rest of your tea-making instructions, and see what you come up with!
Let’s take another look at our kettle-filling algorithm:
1. If the water in the kettle reaches the MAX indicator; Then a. Finish here (there’s nothing to do!) 2. Grasp kettle by outer handle. 3. If the kettle lid is closed; Then a. Push button on the <em>top</em> of the handle to open the lid on top of the kettle. 4. Place open top of kettle beneath cold-water tap. 5. If the cold-water faucet isn’t open; Then a. Open cold-water faucet. 6. Has the water reached the MAX indicator on the kettle? a. No? i. Wait 1 second ii. Go to step 6 b. Yes? i. Continue to step 7 7. Close the cold-water faucet. 8. Close the lid on the kettle.
It’s getting fairly involved now, and it’s only about one sixth of our overall tea-making instructions!
So to save ourselves the trouble of dealing with all this detail in the future, we can put a label on it, and know that the label represents a well thought-out algorithm. What should we call it then?
Fill kettle from tap.
What?! Back here again? This is our step from our first effort at decomposition!
Here’s the difference:
Before, ‘Fill kettle from tap’ was useless to UrkelBot. But now ‘Fill kettle from tap’ contains everything UrkelBot needs to know to fill the kettle.
But we could have just given UrkelBot the original list, right? Machines don’t care if instructions are wrapped up in nice labels.
As I said before, it makes life easier for us.
Having worked out all the detail, we can happily forget it and use our abstraction, confident that now, and in the future, we can tell UrkelBot to ‘Fill kettle from tap’, and he can go and look at our detailed instructions.
And here’s another benefit – we can generalise the abstraction, and the underlying algorithm to give us something like:
Fill from tap (thing)
So that we expand UrkelBot’s abilities to
And so on. Our abstraction not only saves us from thinking work, it expands our (and UrkelBot’s) abilities, so that we multiply the value of the work we did developing the original algorithm.
Over to you – see what abstractions you can come up with from the algorithms you developed in the previous section. How could you generalise and expand them in the future?
So now we’ve have a good look at the fundamental practices for understanding problems and developing solutions:
While I can’t give you an actual UrkelBot (how I wish I could!), I’m sure you realise by now he’s standing in for your computer.
And of course these are the practices you’ll use to develop programs for your computer.