[comp.lang.fortran] EQUIVALENCE, COMPUTED GO TO in FORT

hirchert@uxe.cso.uiuc.edu (12/09/89)

Walt Brainerd (brainerd@unmvax.unm.edu) writes:
>Computed GO TO is on a list of a few noncontroversial things
>with the property that they could be considered for removal
>from the next standard (2001?) after the one coming up.

No, computed GO TO is not on the "obsolescent features" list.  Only
_assigned_ GO TO is on that list.

hirchert@uxe.cso.uiuc.edu (12/12/89)

I'd like to offer a few more points about the "obsolescent features list".

o  If a series of block IFs were the only possible replacements for assigned
   GO TO and alternate RETURN, I doubt whether it would have been possible to
   muster the votes in X3J3 to put them on the obsolescent features list.  In
   both cases, computed GO TO can be used to provide a replacement whose is
   comparable to that of the original feature.

o  PAUSE is a bit of a special case.  In some systems, it is quite true that
   one cannot reproduce its functionality with input/output statements.  (E.g.,
   on one multiuser system I have used, it holds up the entire system until the
   system operator responds!)  However, none of these extended functionalities
   are portable.  The committee concluded that it was better to use the
   input/output statements to get the portable functionality and access the
   special functionalities by calling procedures (written in C or Pascal or
   Ada or assembly language or whatever is necessary to implement that special
   functionality on the given machine).

o  It has been suggested that DO loops with integer indices cannot exactly
   replace the behavior of loops with real or double precision indices.  In
   fact they can.  For example the loop
         DO 10 X=0.0,1.0,0.1
         ...
      10 <some statement>
   could be exactly replaced by
         em1=0.0
         em3=0.1
         iters=(1.0-em1+em3)/em3
         X=em1
         DO 10a it=1,iters
         ...
      10 <some statement>
     10a X=X+em3
   [The variable em1 was not really needed in this case; X could have been
   defined earlier and used in its place.  However, in the general case, the
   expressions controlling the iteration might involve the old value of X, so
   it might not be acceptable to define X earlier.  Similarly, in many cases,
   it may be possible to simply move the label 10 down to the incrementing of
   X, but having two distinct labels will be necessary if the loop contains
   any branches to 10.]  The above is simply an encoding of the description
   of the DO loop found in section 11 of FORTRAN 77.  The only case where it
   might not work would be if the processor handles loops whose number of
   iterations is greater than the maximum value representable by an integer.

   The above characterization of this loop also illustrates what is wrong
   with using real variables as DO loop indices.  The expression for the
   number of iterations can easily give 10 or 11, depending on which way the
   binary representation of 0.1 is rounded.  Even worse, there is an
   accumulation of error in the incrementation of X, so the successive values
   of X can be quite different from what is expected.  The loop could execute
   only 10 times with the final iteration being for a value less than 0.9 or
   the loop could execute 11 times with the final iteration being for a value
   greater than 1.0.  There can be more extreme problems:  For example on an
   IBM mainframe, a loop such as
         DO 20 Y=1000000.,1000001.,0.01
   should execute approximately 100 times with Y equal to 1000000. on every
   iteration because the difference between 1000000. and 1000001. is represent-
   able by the real type, but the difference between 1000000. and 1000000.01
   is not.

   To avoid these kinds of arithmetic problems, it is usually better to replace
   our original loop with something like
         DO 10 it=0,10
         X=0.0+0.1*it
         ...
      10 <some statement>
   This may not produce exactly the same results as the original loop, but in
   cases where they differ, this result is likely to be more accurate and more
   portable.  (If you want to be extra careful, make it X=MIN(0.0+0.1*it,1.0).)

Although computed GO TO is _not_ on the list of obsolescent features, there has
also been some discussion of eventually replacing it with the CASE construct
(some time in the next century).  I would note the following:

*  In my experience, most uses of computed GO TO are to simulate CASE
   constructs, although in many cases this is masked by one of the cases
   being something like an EXIT or CYCLE of a loop.  Obviously, in these cases
   it would be more appropriate to use the CASE construct once it is available.

*  In those cases where a computed GO TO cannot be construed as implementing
   a CASE construct, it is possible to simulate its effect by putting
   unconditional GO TO statements inside a CASE construct.

   . If the number of different branches is significantly smaller than the
     number of different case values, the CASE construct representation may
     still be more readable than a computed GO TO with repeated labels in the
     list.

   . Even in those cases where the CASE representation is more verbose and less
     readable, it should always possible.  The size of a computed GO TO is
     limited by the number of allowed continuation lines, but a CASE construct
     consists of multiple statement and is thus not subject to this limit.

If, in the interests of making the language smaller, we could retain only one
of these features in the language, I would choose the CASE statement because
it appears to be more appropriate more often and would involve no loss of
functionality in those cases where it is not more appropriate.  Fortunately,
however, such a decision, if it ever needs to be made, is decades away.

Kurt W. Hirchert     hirchert@ncsa.uiuc.edu
National Center for Supercomputing Applications

hirchert@uxe.cso.uiuc.edu (12/15/89)

jlg@lambda.UUCP writes:
>The feature that REALLY replaces the assigned GOTO is internal
>procedures.  Except internal procedures can't be nested and
>assigned GOTO can allow nested usage.

In the sense that internal procedures are nested, procedures simulated with
assigned GO TO are not nested at all; i.e. they have no local scope.
Procedures simulated with assigned GO TO can call each other, but then so can
internal procedures.  What is it that you can do with the assigned GO TO that
you think you can't do with an internal procedure?

>MOST uses of the computed GOTO can be done as CASE statements.  Indeed,
>most of my usage of computed GOTO directly emulates CASE statement
>semantics.  However, neither block IFs nor CASE statements allow a
>satisfactory replacement for the following:
>
>      GOTO (101,102,103), iselect
>      STOP 'ISELECT is out of range'
>
>  101 continue
>         [Print the day of week]
>  102 continue
>         [Print the date]
>  103 continue
>         [Print the time]
>
>Here the code has been written to "gradually" change its behaviour
>based on the ISELECT variable.  It either prints just the time, or
>the date and the time, or the day-of-week plus date and time.
>Solutions to do this without the computed GOTO always are harder
>to read and maintain than this is.  I come across this "gradual"
>code shift problem only once in a while, but when I do there's
>nothing better than the existing computed GOTO.

This is very much a matter of taste.  Some people would argue that

      IF (1 <= iselect .and. iselect<=3) THEN
         IF (iselect <= 2) THEN
            IF (iselect <= 1) THEN
               [Print the day of week]
            END IF
            [Print the date]
         END IF
         [Print the time]
      ELSE
         STOP 'ISELECT is out of range'
      END IF

is more descriptive of what you are doing.  Others would argue in favor of

      SELECT CASE (iselect)
      CASE(1)
         CALL PRINT_DAY_OF_WEEK
         CALL PRINT_DATE
         CALL PRINT_TIME
      CASE(2)
         CALL PRINT_DATE
         CALL PRINT_TIME
      CASE(3)
         CALL PRINT_TIME
      CASE DEFAULT
         STOP 'ISELECT is out of range'
      END SELECT

where the logic to print the day of week, date, and time has been moved to
appropriately named internal procedures.  Another might argue in favor of

      itemp=iselect
      DO
         SELECT CASE (itemp)
         CASE(1)
            [Print day of week]
         CASE(2)
            [Print date]
         CASE(3)
            [Print time]
            EXIT
         CASE DEFAULT
            STOP 'ISELECT is out of range'
         END SELECT
         itemp=itemp+1
      END DO

Yet another might replace the above loop with a DO loop starting at iselect.
Someone else might control the who process by a decision table.  Finally, some
people might simply replace your computed GO TO with

      SELECT CASE (iselect)
      CASE(1); GO TO 101
      CASE(2); GO TO 102
      CASE(3); GO TO 103
      CASE DEFAULT; STOP 'ISELECT is out of range'
      END SELECT

and claim that although this is somewhat more verbose it is equally readable 
and maintainable.

Fortunately, computed GO TO is an ordinary part of Fortran 8x, which means
that at worst it would be made obsolescent in the next revision in order to be
removed from the revision following that, so none of us are likely to have to
worry about replacing any computed GO TOs for at least 20 years.

Kurt W. Hirchert     hirchert@ncsa.uiuc.edu
National Center for Supercomputing Applications