FOR-NEXT Loop Etiquette
Jim Butterfield, Associate Editor
If you've ever run into problems with FOR-NEXT loops, maybe it's because you weren't minding your programming manners. This explanation of FOR-NEXT rules should clear things up. Although written for Commodore BASIC, the advice applies to nearly all versions of BASIC.
The FOR-NEXT loop structure is one of the foundations of efficient BASIC programs. It seems to be surrounded by a mystique: Can you or can't you exit a loop before it completes its allotted repetitions?
There's a maxim gaining ground which says: "Never jump out of a FOR-NEXT loop, or sooner or later you'll get an OUT OF MEMORY error." Partly right, partly wrong. You can jump out of a FOR-NEXT loop, but you must understand the rules.
Let's suppose you have a list of 1000 cities around the world. You're writing a program to give the distance between any two cities. The list of city names is in an array called C$, dimensioned to hold 1000 names.
The coding would start by asking the user to enter a city name. Then there would be a search through the table for a name match. The program would partly look like this:
INPUT "ENTER CITY NAME"; N$ FOR J = l TO 1000 IF C$ (J) = N$ … … NEXT J
If the user typed in PARIS, and it happened that PARIS was the second city in array C$, it would seem to be a waste if the program was forced to look at the remaining 998 table entries. On the other hand, if we're forbidden to jump out of the loop (to a statement following NEXT J), we seem to have no choice but to allow the extra 998 iterations.
What Are Our Options?
First, we can indeed exercise the loop over its entire range. The coding would look something like this:
K = 0 FOR J = l TO 1000 IF C$ (J) = NS THEN K = J NEXT J
At this point, K will hold the city number; if the city is not found in the list, K will equal zero. It will work, but the loop will be slow; there will be a significant pause for each city, even if the name is found at the top of the list. It seems inefficient.
Second, we can force the loop variable outside its range on the assumption that this will cause the loop to terminate. The coding would look like this:
K = 0 FOR J = l TO 1000 IF C$ (J) = N$ THEN K = J : J = 1001 NEXT J
This works, but it seems to me to be dangerous. If the city list were expanded to 2000 items, it might be easy to overlook the change that would be needed to the J = 1001 statement. We could fix that part by changing it to J = 1E2O, a high number we never expect to reach.
Changing the value of a loop variable is bad practice. Some languages (even some BASIC implementations) forbid this, and may even stop with an error such as LOOP VARIABLE CHANGED WITHIN LOOP. Here's the problem: The FOR-NEXT loop was designed to allow strict control over the number of repetitions made by the loop. Once we play around with the variable, we endanger the integrity of the loop. Doing this might create a situation where the loop will never end or will behave unpredictably.
Third, we can jump out of the loop when it has done the job we want: found the matching item, or whatever. The coding in this case might go:
K = 0 FOR J = l TO 1000 IF C$ (J) = N$ GOTO 310 NEXT J PRINT "NOT FOUND" : GOTO … 310 continue …
It seems natural, and in many languages it's heartily encouraged. Structured purists might look down their noses at the GOTO statement that gets you out of the loop, but it would be a syntax complaint rather than an objection to leaving the loop early. Very structured language might offer an EXIT command to escape the loop.
Departing from an incompleted loop has developed a bad reputation. The rumor has gotten around that if you do this, the loop will never go away and eventually you'll hang up on an OUT OF MEMORY error. Not true. There is, however, a slight chance that naive coding might produce a baffling NEXT WITHOUT FOR halt; in this case, a little understanding or application of good programming habits will eliminate the danger.
We don't want unclosed loops to hang around forever and clutter up our computer. There are four ways that a FOR-NEXT loop can be retired from service—apart from obvious extreme measures such as turning the computer off or typing NEW.
- When the loop goes through its complete range, it will be scratched from the active loop list.
- If a loop is within another loop, the inner loop will be scratched whenever the outer loop reaches a NEXT statement. Note that this doesn't mean the outer loop must complete its range; if it goes back for another repetition, that too will cancel the inner loop.
- If a loop is opened within a subroutine, RETURN from that subroutine will automatically scratch the loop.
- If a FOR statement is encountered, any existing loop using the same variable name will be scratched, together with any other loops nested within.
100 FOR J = l TO 50 STEP 3 110 T = T + J 120 NEXT J
After these lines are executed, loop J will no longer be active. It has completed its range.
100 FOR 1 = 1 TO 1 110 FOR J = l TO 50 STEP 3 120 T = T + J 130 IF T > 100 GOTO 160 140 NEXT J 150 STOP 160 NEXT I
After these lines are run, loops I and J will no longer be active. Why not? I has gone through its entire range. J has not; but the J loop was scratched from the active list the moment NEXT I was encountered. At first glance, the I loop seems to have no purpose, since there is no repetition of the lines between FOR and NEXT; but it does serve to clean away the J loop.
100 GOSUB 200 110 END 200 FOR J = l TO 50 STEP 3 210 T = T + J 220 IF T > 100 THEN RETURN 230 NEXT J 240 STOP
After these lines are executed (reaching line 110), loop J will no longer be active. Why not? Because it was opened in subroutine 200, and the RETURN at line 220 canceled its status. Program style experts might criticize the subroutine at line 200 because RETURN is not at the end; put it there if you like.
100 FOR J = l TO 50 STEP 3 110 T = T + J 120 IF T > 100 GOTO 150 130 NEXT J 140 STOP 150 K = J 160 FOR J = l TO 1 : NEXT J
When these lines are done, loop J will be inactive, even though the FOR-NEXT at 100–130 was not completed over its range. The opening of a new J loop in line 160 cancels the previous J loop.
In most cases, rule 4 saves most programs from encountering loop problems. Opening a new loop cancels the old one even when we jump out of it. All we need to do is use the same variable name. Often, programs go back and repeat an early section; and the same loops are opened again, with old loops scrapped as the new ones come into force. We hardly need think about the question.
We can make this almost rigorous if we apply a simple rule: Give all your major loops the same loop variable name, and inner loops similar consistent variables. Any big loop will then automatically cancel the previous big loop, and so on.
But if we indulge in "barefoot" coding and pick variables according to the way that letters of the alphabet pop into our heads, we can run into trouble on rare occasions.
Here's a horrible sample program. It doesn't do anything useful, but illustrates the puzzling problem that can occur when we let loops run free.
100 INPUT "YOUR AGE";A 110 FOR J = l TO 99 120 A = A - J 130 IF A<0 GOTO 200 140 NEXT J 150 STOP 200 R = J : T = 0 210 FOR M = l TO R 220 T = T + M 230 FOR J = l TO T 240 V = V + J 250 NEXT J 260 NEXT M 270 PRINT "I WISH YOU";V;"JOYS"
Here's the puzzling thing. This program stops with a NEXT WITHOUT FOR error in line 260. It's baffling to the programmer: The NEXT M is clearly matched with the FOR M in line 210. How dare the computer say they don't match?
Let's carefully trace what happens here, and how an open loop gets us into peculiar trouble.
The FOR-NEXT loop in lines 110 to 140 is not completed; line 130 exits directly to line 200. There's still a live J loop when line 200 is reached.
Line 210 opens a new FOR loop using variable M. Since the J loop is still active—the new one doesn't cancel it—we now have two loops. The outer loop uses variable J and the inner loop uses variable M.
Line 230 wants to open another FOR loop, this time using variable J. But wait a minute; we have an active loop still in existence that uses J. Fine. Cancel the old J loop; that's what rule 4 says. And since the M loop is inside the J loop, it gets canceled too. What do we have now? A brand-new J loop and nothing else. The old J and the M are scrapped.
Line 250 finds the NEXT J statement quite acceptable. There's a J loop active, and it will be exercised however many times the values call for. When the loop completes going through its range of values, it is retired from duty. Now there are no active loops, and we may proceed to line 260.
Line 260 says NEXT M, but the computer doesn't have an active M loop anymore. It was canceled back at line 230, remember? So the computer stops and reports NEXT WITHOUT FOR, causing the programmer to tear out his or her hair.
How do we fix it? Let me count the ways:
- We fix it by rule 1. We change the FOR-NEXT loop at 110 to 140 to exercise its entire range. Line 130 would change to something like: IF A<0 THEN RJ; we'd eliminate the STOP in line 150 and change line 200 to just T0. Slower, but OK.
- We fix it by rule 2. We insert the two lines:
- We fix it by rule 3. The coding from line 110 to 150 is changed to a subroutine. RETURN cancels the open J loop.
- We fix it by rule 4. We could insert a new line 205 that said FOR J = l TO 1:NEXT J; this would certainly cancel the active J loop. It might be better, however, to use our variable hierarchy rule, making J the outer loop through the entire program. Lines 210 to 260 become:
105 FOR I = 1 TO 1 205 NEXT I
The extra loop does nothing but cancel the J loop, but that makes everything OK.
210 FOR J = l TO R 220 T = T + J 230 FOR M = l TO T 240 V = V + M 250 NEXT M 260 NEXT J
Now the FOR J loop at line 210 immediately cancels the open J loop from earlier lines.
But perhaps it's not so much a problem of fixing a program gone wrong. If we develop good programming habits, using systematic variable names, there will never be anything to fix.
Conclusion: You can jump out of FOR-NEXT loops and still be considered a good person. It's sound programming. But you'd be well-advised to understand a little more about how these loops work, and to develop good habits in choosing loop variable names, to banish the possibility of these annoying—and puzzling—program halts.