How+to+avoid+GOTOs

//by Richard Russell, November 2006, revised June 2007 and October 2011//

BBC BASIC includes the **GOTO** statement, but GOTOs are generally considered to be **a bad thing**. I won't repeat the arguments for avoiding GOTOs here: they are well explained in the [|tutorial], on [|Wikipedia] and elsewhere. Suffice it to say that they can result in code which is difficult to understand, difficult to maintain and more likely to have bugs.

Supporters of GOTO will point to code examples such as the following as justification for their use:

code format="bb4w" FOR i = 1 TO maxi FOR j = 1 TO maxj FOR k = 1 TO maxk IF a(i,j,k)=0 THEN GOTO 100 NEXT k       NEXT j      NEXT i      PRINT "Empty element not found" END

100  PRINT "Empty element found at: "i,j,k code What this code does is to scan through all the elements of a 3-dimensional array, looking for the first empty (zero) element. When found it doesn't bother to look any further, and terminates the search by jumping out with GOTO. If it ever finishes the search it means an empty element was never found.

Admittedly it is clear how the code works, and it is one of the more acceptable uses of GOTO, but it is still undesirable (for example it doesn't clear down the stack, and could eventually result in running out of memory). How might we rework the routine to avoid the use of GOTO? One way would be to replace the FOR...NEXT loops with REPEAT...UNTIL loops:

code format="bb4w" i = 0 REPEAT i += 1 j = 0 REPEAT j += 1 k = 0 REPEAT k += 1 UNTIL a(i,j,k)=0 OR k=maxk UNTIL a(i,j,k)=0 OR j=maxj UNTIL a(i,j,k)=0 OR i=maxi

IF a(i,j,k)=0 THEN PRINT "Empty element found at: "i,j,k ELSE PRINT "Empty element not found" ENDIF code This certainly avoids the GOTO, but is it as clear? And what about execution speed? A simple measurement demonstrates that this 'improved' version takes about 40% longer to run.

Perhaps we can stick with using FOR...NEXT loops but avoid the GOTO another way:

code format="bb4w" imt = 0 : jmt = 0 : kmt = 0 FOR i = 1 TO maxi FOR j = 1 TO maxj FOR k = 1 TO maxk IF a(i,j,k)=0 THEN imt = i : jmt = j  : kmt = k              i = maxi : j = maxj : k = maxk ENDIF NEXT k       NEXT j      NEXT i

IF a(imt,jmt,kmt)=0 THEN PRINT "Empty element found at: "imt,jmt,kmt ELSE PRINT "Empty element not found" ENDIF code This relies on the ability to terminate a FOR...NEXT loop prematurely by setting its control variable to the limit value (all versions of BBC BASIC let you do that). Note that it is now necessary to copy the indices at which the empty element was found, but the execution speed is at least as good as the first version (it may be faster, because GOTO itself is quite slow).

Here is a radically different approach:

code format="bb4w" IF FNsearch(i,j,k) THEN PRINT "Empty element found at: "i,j,k ELSE PRINT "Empty element not found" ENDIF END

DEF FNsearch(RETURN i,RETURN j,RETURN k)     FOR i = 1 TO maxi FOR j = 1 TO maxj FOR k = 1 TO maxk IF a(i,j,k)=0 THEN = TRUE NEXT k       NEXT j      NEXT i      =FALSE code This time we've moved most of the code into a user-defined function. Now we can exit from the loops without using a GOTO, simply by returning early from the function.

The code is easy to understand, it runs quickly, and by moving the search routine into a function (which can be placed out of harm's way at the end of the program) we also adhere to one of the other principles of modern software practice: [|encapsulation]. The theory is that it is better to move self-contained functional modules out of the main code so they don't clutter it and can be separately maintained.

There are however a couple of problems with this code. Firstly, some versions of BBC BASIC won't let you jump out of a function without first 'unrolling' the FOR...NEXT loops (BBC BASIC for Windows will). Secondly some people think it is cheating, because it's effectively still jumping out of the loops - just not using GOTO to do so!

Finally, if you're using //BBC BASIC for Windows// version 5.60 or later, you can make use of the [|EXIT] statement added in that version:

code format="bb4w" FOR i = 1 TO maxi FOR j = 1 TO maxj FOR k = 1 TO maxk IF a(i,j,k)=0 THEN EXIT FOR i         NEXT k        NEXT j      NEXT i

IF i <= maxi THEN PRINT "Empty element found at: "i,j,k ELSE PRINT "Empty element not found" ENDIF code I hope you can see that there are several ways of avoiding GOTO. Not all of them necessarily improve a program's structure, but there is invariably one that has all the advantages but none of the disadvantages of using GOTO.