Create Inner Loops To Match the Natural Logic Of What You Are Doing

Nested loops solve more complicated problems than non-nested loops. For that reason, when we first see them, we usually see more complicated problems than we had seen up to that time. This plants a subliminal message in our brains: "nested loops are difficult". That's wrong. They are often simpler than the alternatives. But, to achieve that simplicity, they must mimic the natural logic of what you are doing.

In this example, we write a short program to copy a file while prepending two characters

 "| " 
to every line. Here, is pseudocode for one possible approach:
 

read Ch  

WHILE Ch != END_OF_FILE DO

   IF Ch = END_OF_LINE THEN

        write Ch

        read Ch

        IF Ch != END_OF_FILE THEN

            write "|  "

        FI

   ELSE

        write Ch

        read Ch

   FI

END  

This code is fallacious. How easily can you see why?

Once you have seen why, you might be tempted to solve the problem by adding

 

write "| "  

to the beginning of the pseudocode. This isn't a good idea for two reasons. First, it won't properly handle the case in which the input file is empty. Second, it perpetuates the real problem which is that the single loop structure does not match what the program is supposed to do.

Since the program copies lines and takes an action for each line copied, it is quite clear that line-by-line processing is natural. Pseudocode that uses line-by-line processing is:

  

read Line  

WHILE Line was input DO

    process and write Line

    read Line  

END  

or -- depending on your language's i/o paradigm -- like this:

  

WHILE there is more input DO

     read Line

     process and write Line

END  

From this pseudocode you might well choose to use library functions for reading and writing lines. Here is the solution using the second i/o paradigm:

  

WHILE there is more input DO

    read_line LINE

    write "| "

    write_line LINE

END  

Isn't the correctness of this code easy to see at a glance?

Because read_line and write_line are easy to use and because they are readily available, they would make it easier for you if you need to switch from one of these two i/o paradigms to the other at some future date. However, in an object-oriented world you can easily fake one paradigm with the other so the possibility of having to make the switch at some future date need not carry much weight.

If tight code is important, you will not want to use these subprograms. That, however, is no excuse for not using loops in a natural way. Here is an example using the first i/o paradigm.

  

read Ch  

WHILE Ch != END_OF_FILE DO

    write "| "

    REPEAT

       write Ch

       read Ch

    UNTIL Ch = END_OF_FILE OR Ch = END_OF_LINE

END  

Here, the inside loop can be said to "copy the line" and the correctness of the program is easier to see than the incorrectness of the first version -- even though this version uses nested loops while that version does not.

It requires some thought to create inner loops that match the natural logic of what you are doing. Your solution will depend somewhat on your other goals. The payoff, however, comes when you have to check whether you have written a correct program. This payoff may seem small with a small example. Taking the same kind of care with a large program, however, pays a large dividend.

Copyright and Permissions

Copyright, 1995 by J Adrian Zimmer

This tip is distributed to individuals free of charge from the Internet Themes web site. All other distribution (including but not limited to internal distribution within an organization and mirroring of any kind) is forbidden without written consent of the copyright holder.

Return top of this document.

Context  Some Tips for Programmers    Author J Adrian Zimmer  
Dated: July 26, 1995 ; Revised: Oct 07 1998