Updated Jan. 26: "FOR-NEXT" Best Practices
Many of you probably have seen the flurry of notes on FOR-NEXT over the last several days. The purpose of this post is to document best practices, so you can ignore all those posts.
I have honestly been shocked at all the different ways FOR-NEXT is implemented. It’s very easy to fall into a trap if you’re not careful. If you want your programs to work on multiple machines, or even use different compilers on the same machine, you should follow this guidance. This is my first attempt at making a complete list. Please leave a comment on this post, or FaceBook if I have missed anything. I will spare you all the crazy things I experimented with this week. Many of you told me that is bad code. I know that I was pushing BASIC to its limits to figure out what good code is.
These guidelines are for older BASICs, typically ones that use line numbers, GOTO, and GOSUB. If you are using a modern BASIC that supports recursive SUBs and FUNCTIONs, use them! Don’t fall back on old habits. For example, QB64 is a modern BASIC, but it can also do line numbers, GOTOs, and GOSUBs. Don’t use them!
Here’ my proposed list:
Always enter a FOR-NEXT loop at the FOR. Don’t even think about entering the middle of a loop. Not surprisingly, “there be dragons here” if you do. See the comment from Peter McGavin below that shows the crazy results you may get depending on the interpreter or compiler.
Avoid leaving FOR-NEXT in mid-flight. Use FOR-NEXT to count from the beginning to the TO value with steps of STEP. If you feel you must leave a FOR in mid-flight, there are two approaches. If you have a complex situation, I’d suggest using a WHILE statement instead:
let i = start
while i <= stop
... body ... (may jump out.)
let i = i + step
wend
I'll leave it as an exercise how to re-swizzle this when counting down.In
If you need to leave a FOR-NEXT and the loop is otherwise pretty simple, use a pattern like below. It was suggested that this is a common idiom that first appeared in the 80s.
for i = strt to nd step st
...
...
if we_need_to_leave then
i = nd+1
end if
next i
If you intend to jump out and then return to the body of the FOR, a good approach is to use a GOSUB to jump out and then RETURN to where you were. Make sure the subroutine does not change the value of the control variable.
for i = 1 to 10
if i = 3 then gosub 100 ' at 100 don't do any "for i"s or "let i =".
next i
end
100 ...
110 return
Never enter a FOR loop recursively, unless you are using a modern BASIC, and even then use recursive subs/functions and not GOTOs or GOSUBS:
10 rem ************ DON'T DO THIS **********
20 ... some code ..
30 for i = 1 to 10
35 ...
40 gosub 20 ' we're trying to trick BASIC into starting another instance
45 ' of "for i", leaving two or more "for i"s in-flight. This is
' undefined behavior. You may get away with this on one BASIC,
' and find your code does not work on another. Even worse,
' you may get different results.
50 next i
Don’t try to change the final value or the step when a FOR-NEXT is in flight. You may get away with this on some BASICs and not on others.
10 rem ****************** DON'T DO THIS **********
20 for i = strt to nd step s
30 ...
40 nd=100: s = 5
50 ..
60 next i
Draw a line from each FOR to the corresponding NEXT, and make sure no lines overlap, as depicted below:
+-- for i = 1 to 10
|
+----|--------for j = 1 to 10
+ |
+ +---next i
+
+-------next j
It’s good practice to have one NEXT for every FOR. Some BASICs require this. Think of the FOR-NEXT as a lexical structure that delimits a block of code. For this reason, it’s also good practice to use the control variable on the NEXT, for example, “NEXT I”. This is also required by some BASICs.
I updated this on January 26, 2024, based on comments I received. If I get any more comments I will add them as well. I’ll treat this as a living document.
Thanks for reading my blog!
I think this is another anti-pattern that should be avoided, particularly if you want it to be portable across BASICs. I hate to say it, but I feel this should be another NO NO on my list. FOR and NEXT, going back to Dartmouth, was intended to be a lexical block structure. In other words FOR appears at one spot, and there is one NEXT further down in the code. A variable name was required in the NEXT to insure a block structure. NEXT was like a curly brace in C. It was not intended to be an executable instruction. It was intended to close a block.
This problem was eliminated in 1983 (for everyone) and earlier (at Dartmouth). Dartmouth BASIC the Sixth and ANSI/ISO Standard BASIC, have an EXIT FOR statement that keeps the FOR-NEXT stack clean. Even Microsoft BASIC has this command now.