LoopDebates

From Pickwiki
Revision as of 23:48, 26 February 2015 by Conversion script (talk) (link fix)
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Jump to navigationJump to search

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.