Most programs involve repeating a series of instructions over and over until some event occurs. For example, if we wish to read ten numbers and compute the average, we need a loop to count the number of numbers we have read. Count loops are loops where the program must count the number of times operations are completed. The flowchart below illustrates a loop that counts from 1 to 10:
Count loop flowchart
While count loops work the exact number of times needed, in many cases we do not know how many times we want to do something. It is often dependent on the data provided to the program. Imagine we change our problem to read and compute the average of a number of numbers. We won't know how many numbers there are but will read numbers until there are no more. Both these assume that the computer will tell the program when there is no more numbers. This is called an end-of-data or end-of-file test. There is an important difference between the pre-test and post-test loops. The pre-test version will work even if there are no numbers, the post-test version assumes the body of the code will be obeyed at least one time. Both forms of loops are appropriate in different circumstances. Looping with switching and goto's The looping and switching logic above follow well defined rules. In fact, we can implement any of these constructs with a condition and a goto (unconditional branch) instruction. An example of this logic is was illustrated in the loop flowchart shown previously. Early programs were written this way. As the problems became more complex it became impossible to follow the logic when things go wrong. Imagine trying to sort out code like this:
step 01: do something
step 02: go to step 16
step 03: do something
step 04: if some event has occurred go to step 19
step 16: if some event has occurs go back to step 4
step 17: go to step 1
step 18: do something
step 19: if something is greater than 10 goto step 1
step 20: go to step 1
Spaghetti code was born! The rule is simple, you avoid goto statements but use the higher level
constructs we have introduced here for switching logic and looping logic. Even with these, sorting
out the logic in a program can be quite excruciating. However, it is always good fun when you find
eventually the error!.
You notice that the logic is nested. Within a loop we may have switching logic and within that
switching logic we may again have a loop, and so it goes. Program algorithms are usually
hierarchical in nature. The style of programming we are using is called procedural, because we
define the solution in the firm of a procedure indicating what to do. Most problems require this
approach. There are programming methods (or paradigms) where you express the problem
algebraically, or as formal logic. A compiler like program will then solve the problem for you. A
skilled programmer will use the appropriate method for the nature of the problem being solved.
As logic gets deeply nested it becomes difficult to follow what is happening. As a general rule,
logic should not be nested more than 3 or 4 levels deep. The way to avoid these problems is with
the use of functions.
Functions (or subroutines, or sub programs) are named chunks of logic that do something. It is
the bundle of sticks issue again. We break our big problem into many smaller problems that can
be easily solved. These become our functions. We then use these functions to solve the big
problem. You may have noticed that we can more easily test our solutions this way. We can test
the functions separately, and then use the working functions to build a larger program. This is far
easier than trying to find out what has gone wrong when you have many hundreds of instructions
interacting, and all you know is that something unexpected has happened.
We have covered an immense amount of ground. The way it will become real is in use. You can
read as many books about repairing cars as you like, but until you actually do it, you won't see
the value of the written advice.
Practice and enjoy, and be kind to yourself, it can be very frustrating.... hehehe ^_^