CS 3723/3721 Programming Languages
Recitation 1: Nogoto


Overview: This recitation is concerned with the GOTO statement, which is a part of most languages, with the exception of Java. More than 30 years ago, computer scientists recognized that programs written with GOTOs were often much more difficult to comprehend than those without GOTOs. This was part of the structured programming revolution. For this reason the use of the GOTO statement has been discouraged, to the point where the statement is often not even taught to beginning C or C++ programmers. (Of course its use in Java is not possible.)

One issue that might bother students is: what if you had a program written with GOTOs whose logic was so complicated that you could not understand it. It turns out that there are powerful theoretical results which show that any program written using GOTOs can be rewritten without GOTOs so that it carries out exactly the same computation. It is important to realize that it is not necessary to understand the program in order to rewrite it.

This recitation illustrates the above result starting with a moderately confusing initial program with GOTOs. The whole idea is not to unravel the logic of this weird program, but to see that it can be transformed to a program without GOTOs, without understanding how the program works.

Several examples below ocurr in Fortran and in Pascal. It is not necessary to completely understand either Fortran or Pascal in order to understand these examples.

Initial example: A Fortran program that translates an integer to Roman numberals: Consider the following Fortran program. This program uses only GOTOs and IFs followed by a GOTO. It translates an integer less than 4000 into Roman numerals. (The line numbers at the right have nothing to do with the program itself.) Note that this is intended to be an obscure program, whose mechanism may not be clear.

runner% cat rom.for
* Fortran program to translate to Roman numberals
      PROGRAM ROMAN
      INTEGER ROM, I, L, N, K
      CHARACTER*1 S(15), R(7)
      DATA R /'I','V','X','L','C','D','M'/
      ROM = 3947                                   Line 1
      I = 1                                        Line 2
      L = 1000                                     Line 3
      N = 13                                       Line 4
20    IF (MOD(N,4) .NE. 1) GOTO 30                 Line 5
      K = L                                        Line 6
      L = K/10                                     Line 7
30    K = K - L*(MOD(N,4)-1)**2                    Line 8
40    IF (ROM .LT. K) GOTO 60                      Line 9
      ROM = ROM - K                                Line 10
      IF (MOD(N,2) .NE. 0) GOTO 50                 Line 11
      S(I) = R(((N-2)/4)*2 + 1)                    Line 12
      I = I + 1                                    Line 13
50    S(I) = R((N+2)/2)                            Line 14
      I = I + 1                                    Line 15
      GOTO 40                                      Line 16
60    N = N - 1                                    Line 17
      IF (N .GE. 1) GOTO 20                        Line 18
      PRINT *, S
      STOP
      END
runner% f77 -o rom_for rom.for
rom.for:
 MAIN:
runner% rom_for
 MMMCMXLVII

An equivalent Pascal program without GOTOs: The following Pascal program uses a simple method to carry out the actions of the original program without using GOTO statements. It illustrates a general strategy for translating any program using GOTOs into an equivalent program without GOTOs. ("Equivalent" means that the new program carries out exactly the same computation.) Thus in a sense it shows that the simple-minded approach to structured programming ("Get rid of all GOTOs") can always be achieved.

Here the variable LINE keeps track of which of the original 18 lines are executing. Each case (1-18) corresponds to a line number. After executing a case, the program either increments LINE to the next line number, or else changes LINE to a different line number altogether, corresponding to a GOTO statement.

Do you think the program below is better because it has no GOTO statements?

runner% cat rom.pas
(* Transformed Pascal version of the   *)
(*   original Fortran program          *)
(* No goto statements in this program  *)
program ROMAN;
var
    ROM, I, L, N, K, LINE: integer;
    S: array[1..15] of char;
    R: array[1..7] of char;
begin
    R := 'IVXLCDM';
    LINE := 1;
    while LINE <> 19 do
    case LINE of
     1: begin ROM := 3947; LINE := LINE + 1 end;
     2: begin I := 1; LINE := LINE + 1 end; 
     3: begin L := 1000; LINE := LINE + 1 end;
     4: begin N := 13; LINE := LINE + 1 end;
     5: if (N mod 4) <> 1 then LINE := 8 else LINE := LINE + 1;
     6: begin K := L; LINE := LINE + 1 end;
     7: begin L := K div 10; LINE := LINE + 1 end;
     8: begin K := K - L*((N mod 4) - 1)*((N mod 4) - 1);
         LINE := LINE + 1 end;
     9: if ROM < K then LINE := 17 else LINE := LINE + 1;
    10: begin ROM := ROM - K; LINE := LINE + 1 end;
    11: if (N mod 2) <> 0 then LINE := 14 else LINE := LINE + 1;
    12: begin S[I] := R[((N-2)div 4)*2 + 1]; LINE := LINE + 1 end;
    13: begin I := I + 1; LINE := LINE + 1 end;
    14: begin S[I] := R[(N+2)div 2]; LINE := LINE + 1 end;
    15: begin I := I + 1; LINE := LINE + 1 end;
    16: LINE := 9;
    17: begin N := N - 1; LINE := LINE + 1 end;
    18: if N >= 1 then LINE := 5 else LINE := LINE + 1
    end; (* case  and  while*)
    writeln(S)
end.
runner% pc -o rom_pas rom.pas
runner% rom_pas
MMMCMXLVII

The Pascal program below is an almost exact copy of the original Fortram program, rendered in Pascal.
runner% cat rom3.pas
(* Pascal program exactly equivalent    *)
(*   to the original Fortran program    *)
program ROMAN;
var
    ROM, I, L, N, K: integer;
    S: array[1..15] of char;
    R: array[1..7] of char;
label
    L1, L2, L3, L4, L5, L6;
begin
L1: R := 'IVXLCDM';
    ROM := 3947;
    I := 1; 
    L := 1000;
    N := 13;
L2: if (N mod 4) <> 1 then goto L3;
    K := L;
    L := K div 10;
L3: K := K - L*((N mod 4) - 1)*((N mod 4) - 1);
L4: if ROM < K then goto L6;
    ROM := ROM - K;
    if (N mod 2) <> 0 then goto L5;
    S[I] := R[((N-2)div 4)*2 + 1];
    I := I + 1;
L5: S[I] := R[(N+2)div 2];
    I := I + 1;
    goto L4;
L6: N := N - 1;
    if N >= 1 then goto L2;

    writeln(S)
end.
runner% pc -o rom3_pas rom3.pas
runner% rom3_pas
MMMCMXLVII

A second way to eliminate GOTOS: The Pascal program below (that includes no goto statements in it) uses a different strategy to translate a program with gotos into an equivalent program without goto statements.

Here each block of statements corresponding to the target of a goto is treated as a unit, rather than focusing on the individual lines.

runner% cat rom4.pas
(* Equivalent Pascal version of the          *)
(*   original Fortran progra                 *)
(* Illustrates another translation strategy  *)
(* No goto statements in this program        *)
program ROMAN;
var
    ROM, I, L, N, K, LABEL: integer;
    S: array[1..15] of char;
    R: array[1..7] of char;
begin

    LABEL := 1;
    while LABEL <> 0 do
    case LABEL of

 1: begin
        R := 'IVXLCDM';
        ROM := 3947;
        I := 1; 
        L := 1000;
        N := 13;
        LABEL := 2
    end;
 2: begin
        if (N mod 4) <> 1 then LABEL := 3 else
        begin
            K := L;
            L := K div 10;
            LABEL := 3
        end
    end;
 3: begin
        K := K - L*((N mod 4) - 1)*((N mod 4) - 1);
        LABEL := 4
    end;
 4: begin
        if ROM < K then LABEL := 6 else
        begin
            ROM := ROM - K;
            if (N mod 2) <> 0 then LABEL := 5 else
            begin
                S[I] := R[((N-2)div 4)*2 + 1];
                I := I + 1;
                LABEL := 5
            end
        end
    end;
 5: begin
        S[I] := R[(N+2)div 2];
        I := I + 1;
        LABEL := 4;
    end;
 6: begin
        N := N - 1;
        if N >= 1 then LABEL := 2
        else LABEL := 0;
    end
    end; (* case and while *)
    writeln(S)
end.
runner% pc -o rom4_pas rom4.pas
runner% rom4_pas
MMMCMXLVII

Here finally is the C version of the original Fortran program. Notice that it would not be possible to write this program directly in the Java language, because Java has no goto statement.
runner% cat rom.c
/* C version of the original Fortran program */
#include <stdio.h>
int rom, i, l, n, k;
char s[15];
char r[8]= " ivxlcdm"; /* initial blank because of C's 0-based arrays */
int main()
{
    rom = 3947;
    s[0] = ' ';
    i = 1;
    l = 1000;
    n = 13;
L2: if ((n%4) != 1) goto L3;
    k = l;
    l = k/10;
L3: k = k - l*((n%4) - 1)*((n%4) - 1);
L4: if (rom < k) goto L6;
    rom = rom - k;
    if ((n%2) != 0) goto L5;
    s[i] = r[((n-2)/4)*2 + 1];
    i = i + 1;
L5: s[i] = r[(n+2)/2];
    i = i + 1;
    goto L4;
L6: n = n - 1;
    if (n >= 1) goto L2;
    s[i] = '\0';
    printf("%s\n", s);
}
runner% cc -o rom rom.c
runner% rom
 mmmcmxlvii

Recitation work: For this recitation, you are to translate this program into a Java program using one of the two strategies illustrated above. You should not translate to a structured version of the program even if you could figure out how to make it structured. In case you don't know any Java, you may translate to the C language. (In either case the program must have no goto statements.)

What to turn in to the recitation instructor: Turn is a source listing along with the results of a run.

Key idea: It always possible to mechanically translate a program with GOTOs into an equivalent program without GOTOs, without necessarily understanding the logic of the program. This will work no matter how complicated the original program is.


Revision date: 2002-07-28. (Use ISO 8601, an International Standard.)