LoopDebates
HomePage>>SourceCode>>BasicSource>>LoopDebates
There is often a heated debate in the "pick" world over many forms and structures within the Pick/Basic language. This happens because there are always several ways to achieve something. Let's take, for instance, the common 'loop'. The essence of programming is to devise a procedure to perform some change / analysis on a given type of data, and then get the program (procedure) to do the same thing to ten, hundreds or more "records" containing the same type of data. This is done via the loop.
The easiest or most commonly used loop is the GOTO ...
10 A = 0 11 if (A > 9) then goto 99 print RECORD<A> A = A + 1 goto 11 99 print 'done'
In this structure a label is set, something is compared, if the condition is not met the process moves down to perform some action, a goto statement returns to the label, the process repeats until the condition is met. At least that's a neat example - what often occurs is that, nested somewhere within lies another statement that 'goto's to a place somewhere else in the program, resulting in what is known as "spaghetti" programming (ie code that is not easy to follow, and therefore not easy to maintain or change). THIS TYPE OF CODE IS NOT DESIRED!
Another loop structure is the FOR...NEXT loop
for A = 1 to 9 print RECORD<A> next A print 'done'
In this structure a variable is chosen to be incremented from the value after the '=' to the value after the 'TO'. The range in this example is defined, ie it has a preset beginning and end. A more normal example would have a variable set by counting the number of elements in an array, eg
Elements = dcount(Array, delimiter) for A = 1 to Elements ... next A
The FOR... NEXT works well, but had the problem that, until recently, caused a bottleneck in that in processing a dynamic array, eg RECORD (from the above example), the process would start counting from element 1 for each iteration. What this means is that, in order to process just 10 elements in an array, the program has to process
Element1+ (Element1+Element2)+ (Element1+Element2+Element3)+ (Element1+Element2+Element3+Element4)+ (Element1+Element2+Element3+Element4+Element5)+ (Element1+Element2+Element3+Element4+Element5+Element6)+ (Element1+Element2+Element3+Element4+Element5+Element6+Element7)+ (Element1+Element2+Element3+Element4+Element5+Element6+Element7+Element8)+ (Element1+Element2+Element3+Element4+Element5+Element6+Element7+Element8+Element9)+ (Element1+Element2+Element3+Element4+Element5+Element6+Element7+Element8+Element9+Element10) == 55 processes
In UniVerse, dynamic arrays have a hint structure that remembers the most recent field that was extracted, so a new search can begin from there rather than from the start. Note, however, that this works only for field-mark (attribute-mark) delimited dynamic arrays, not for value-mark delimited arrays or any other delimiter.
A third loop structure is the LOOP ... REPEAT
Equate False to 0, true to 1 done = False loop readnext KEY else done = True while not(done) read RECORD from HANDLE, KEY then print RECORD<8> end repeat
This wider example shows a variable called KEY being populated from a READNEXT, and then used used to read a variable called RECORD from a previously opened file (opened to an internal file handle called HANDLE) from which field 8 is extracted (on a successful read) and printed.
The flag variable can be eliminated, resulting in cleaner code (yes, I know, that's a value judgment!) by using the "Boolean context" of the READNEXT statement - though this may not be available in all MV databases it's certainly possible in UniVerse and UniData.
loop while readnext key read RECORD from HANDLE, KEY then print RECORD<8> end repeat
And yet another version of LOOP/READNEXT that doesn't have a flag variable:
LOOP READNEXT K.FILE ELSE EXIT READ R.FILE FROM F.FILE, K.FILE THEN ;* Do something with record END REPEAT
The LOOP ... REPEAT in this form has the same failings as the FOR ... NEXT above.
There is however another form of LOOP ... REPEAT that is made especially for processing the dynamic array!
Equate False to 0, True to 1 more = False loop remove ELEMENT from ARRAY setting more {syntax varies from flavour to flavour of Pick} print ELEMENT while more repeat
Here the language keeps a positional pointer as each element is removed from the array, meaning that a 10 element array will necessitate 10 steps.
Something to be aware of is that in the first LOOP ... REPEAT example a variable "done" is set and compared immediately after the readnext, whereas in the LOOP... REMOVE example the variable "more" is only compared immediately before the REPEAT.
This is necessary because ...
...in a REMOVE the final setting of "more" is zero, meaning that the final element removed is still unprocessed right after the REMOVE, yet the flag is set to ZERO because there are no more elements to be REMOVEd. One needs to process that final element before exiting...
whereas
...in a READNEXT the flag is set only when all elements have been processed and the readnext falls off the end of the list. Thus one can set the process to exit immediately when the flag is set.
Some programmers avoid this issue using a "priming read", where the first attempt is made outside the loop.
remove ELEMENT from DYNARRAY setting MORE loop while MORE gosub process.element remove ELEMENT from DYNARRAY setting MORE repeat
This also avoids problems in using the CONTINUE statement; the former structure could go into an infinite loop because CONTINUE always goes back to the top of the loop and may never test the WHILE condition.