Principles of practice
Part 1 of this series made the point that getting a problem solved by a computer requires developing a precise, detailed, and fixed plan of action in the form of a program, which is a drastically different form of thinking as compared to how we solve problems as human beings.
We need practice in order to learn this form of thinking. We need to challenge ourselves into programming more and more complex problems. Further, to make this practice effective, we need to follow a few principles while solving a programming problem:
Firstly, to get a fair idea of many details of what the program should be doing, we should be able to manually solve the given problem (usually on paper):
- In a way conceptually close to the one we plan to eventually program,
- In a variety of cases of inputs,
- Repeatedly and systematically.
Although we should use the method that we plan to use in our program, otherwise the focus here should be to solve the problem ourselves, without bothering about the programming details, since the purpose is to get a picture of how the solution proceeds. Further, this exercise is useful even if the algorithm that we are going to use is well known, because what this process gives us is experience, which is much more valuable than just the in-text knowledge. Another aspect that this exercise clarifies is what exactly we are trying to produce: a clear picture of the desired outcome. Without it, programming efforts lack a clear direction.
The next principle, and the most important one, is to observe and note down the systematics. We may make some observations during the first phase, but unless we focus on it, we will not get it all out. Again, this is rarely done apart from programming, so it needs conscious attention. Break all the reasoning down to pieces, till you get a feel that each of them is mechanically doable and there is no “mind” or “intuition” required to do it: try to trace the jumps in intuition. The same thing applies for combining these pieces: it should also be possible in a mindless way. This is how the organization of the program emerges.
After this comes the relatively easy part: to translate all the pieces and ways of combining the pieces into what is available in the programming language we are using. Note that it is easy only when we have isolated thinking about the program organization from it.
It is still not possible, after all this exercise, to guarantee that we have covered all the cases, or our program is perfect. This is natural because of our way of problem solving: we adapt rather than fully plan. This is true not just for novice programmers, but all of them. That is why debugging and improving are part of the central activities in the software world. Once we get comfortable with the fact that no program is perfect, we gain confidence that we will still produce something useful, in spite of the fact that it will constantly need corrections and improvisations.
Here is the principle behind the principles: as mentioned in part 1 we tend not to think about all the details right away. Such a requirement of details sometimes makes programming overwhelming for novices. The process of manually solving the problem and analyzing how we solved it take us through many details which will help us build the program. Further by making the steps conscious and separate, we avoid the overwhelm of facing it all at once when we directly start programming.
Further, with practice, the complexity of the pieces of a program that can be handled right in the head increases. But for most of the problems, some kind of clarification remains necessary, and is a significant part of the work, before we jump into the popular image of a geek focused hard onto the screen and typing like mad into the keyboard.
Part 3 of this series takes a view of a few established techniques for managing the complexity of larger programs.