|The Trick Of The Mind - Modular Programming|
|Written by Mike James|
|Tuesday, 05 September 2023|
Page 1 of 3
Breaking tasks down into little pieces is a really great idea and programming languages need a way to make this easy. The solution is modules, subroutines, procedures or functions - all the same basic idea. This is an extract from my book Trick of the Mind which explores what it is to be a programmer.
The Trick Of The Mind - Programming & ComputationalThought
Buy Now From Amazon
To solve many problems all you need to do is write a program – a simple list of instructions of what to do. Sounds easy, but when you are confronted by a difficult problem – play a game of chess say – then you are stuck. A blank page is always the hardest to fill, once you have some words it is easy to add more. So it is with problems — a partial solution is easier to extend than it is to make a start. In this chapter we look at the organizational methods used in modular top-down programming and discover that they can make any problem instantly soluble.
How New Commands Are Made
All programming languages are initially limited to a few relatively small number of instructions. These are the basic commands of the language and they generally include all of the control statements, If, While and so on, arithmetic expressions and usually basic interaction instructions like Print, Input and so on. You really don’t need anything else, but you might want more. For example, suppose you are writing a program to control a coffee-making machine. You could use some basic instructions to put together a program that makes a cup of coffee. A basic program might follow the lines of:
get cup dispense coffee into cup If sugar Then add sugar stir
This is fine – it does the job. But wouldn’t you really like a language that has a MakeCoffee command that completes the task in one instruction? Yes, of course you would, but if the language had a command for everything you wanted to do in the past or the future it would be impossibly big.
A good compromise is to provide a facility where a list of instructions can be given a name and used by simply writing the name in another list of instructions. Such “packets” of instruction are usually called subroutines, functions, procedures or modules. For the rest of this chapter we will use subroutine. Although it isn’t the most common term today, it is language-neutral. For example:
Subroutine MakeCoffee get cup dispense coffee into cup If sugar Then add sugar stir
defines the subroutine MakeCoffee. Now our program becomes:
Simple and to the point but if you want to find out what MakeCoffee involves you have to go off and find its definition and read it. Thus you have a simplification in that your program is now a single instruction and you have a complication in that to find out what this means you have to go and study another program perhaps stored somewhere else.
In most cases the simplification is more than worth the extra complication because if you can just accept that the subroutine does what it says then you don’t have to “drill down” into its definition. You can just accept the top-level view of what is to happen. For example, imagine you want five cups of coffee. Without the subroutine this would be:
Repeat 5 times get cup dispense coffee into cup If sugar Then add sugar stir
With the subroutine it is just:
Repeat 5 times MakeCoffee
This is a good improvement – shorter programs are always easier to understand and, even if you don’t know what MakeCoffee does exactly, you can see that it is done exactly five times.
The power of subroutines is that they simplify programming by giving you custom commands that do exactly what you want. This means you can take a lot of instructions and reduce them to a single instruction. This is a huge simplification, but only if you don’t look “inside the box”. That is, as long as you don’t want to examine what the subroutine actually does and just accept that it does it, things are simple. In particular, the flow of control is simpler with the subroutine – it simply goes to the first instruction in the subroutine, follows whatever the subroutine does and then returns to the instruction after the one that called the subroutine. For example:
You can see that the first MakeCoffee instruction transfers control to the subroutine. This executes and control passes through the subroutine until it reaches the end when it returns to the instruction following the MakeCoffee instruction. In the jargon we say that the first MakeCoffee instruction “calls” the subroutine and the final instruction returns to the instruction after the call. Notice that most languages use an explicit Return instruction to mark the end of the subroutine. Also notice that when we reach the second MakeCoffee call control is transferred to the subroutine again but this time the return is to instruction2. The subroutine “remembers” where it was called from and returns to the instruction following the call.
The use of the explicit Return instruction also introduces the possibility of using it elsewhere in the subroutine, not just at the very end. For example:
Subroutine MakeCoffee If no coffee available Then Return get cup dispense coffee into cup If sugar Then add sugar stir Return
The use of Then Return “short circuits” the subroutine in that it returns without doing anything because what is to be done can’t be done. This is a very common use of the Return and some think its wonderful because it simplifies things and others are of the opinion that is sacrifices the simplicity of always having the return at the end where everyone knows where to find it. Without the conditional Return the subroutine would be more complicated:
Subroutine MakeCoffee If coffee available Then get cup dispense coffee into cup If sugar Then add sugar stir Return
but not significantly so. In the real world compromises with rules that represent “best practice” are always justified in the name of simplicity.
|Last Updated ( Tuesday, 05 September 2023 )|