(Click or use local copy.)

 CS 3721, Spring 2004
 Programming Languages

 Recitation 1: January 12, 14
 Nogoto
    MW   09:00-09:50 pm, SB 3.01.04
 Due: 2004-01-19 23:59:59

Recitation 1 must be submitted following directions at: submissions on or before
  • 2004-01-19  23:59:59 (that's Monday, 19 January 2004, 11:59:59 pm) for full credit.
  • 2004-01-23  23:59:59 (that's Friday, 23 January 2004, 11:59:59 pm) for 75% credit.


1. 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, initiated by E. Dijkstra, a Dutch computer scientist: Dijkstra's original article. 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 one can always rewrite to get a GOTOless program.

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.

The structure of the resulting transformed program is at least as confusing as the original program, even though the new program has no GOTOs. This shows that the structured programming revolution was not really about making programs free of GOTOs, but was concerned with creating programs whose control structure could be understood.

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


2. Initial example: On the left below is an obscure Fortran program with GOTOs that translates an integer to Roman numerals, while on the right is an equivalent Pascal program without GOTOs. The Fortran program uses only GOTOs and IFs followed by a GOTO. It works for integers less than 4000. (The line numbers in yellow at the right of lines of the Fortran program have nothing to do with the program itself.) Note that this is intended to be an obscure program, whose mechanism may not be clear.

The Pascal program below on the right 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 on the right below is better because it has no GOTO statements?

Fortran Program With GOTOs Equivalent Pascal Program Without GOTOs
% cat rom.for
* Fortran program:
*   translate to Roman numerals
*   uses GOTOs extensively
      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 30Line 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 50Line 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
% f77 -o rom_for rom.for
rom.for:
 MAIN:
% rom_for
 MMMCMXLVII
% 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.
% pc -o rom_pas rom.pas
% rom_pas
MMMCMXLVII


3. A second way to eliminate GOTOS: For a second, slightly different method to eliminate GOTOs, consider the Pascal program below on the left, which is an almost exact copy of the original Fortram program, rendered in Pascal.

The Pascal program below on the right (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.

Pascal Program With GOTOs Equivalent Pascal Program Without GOTOs
% cat rom3.pas
(* Pascal program exactly equivalent    *)
(*   to the original Fortran program    *)
(*                                      *)
(*   Uses GOTOs                         *)
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.
% pc -o rom3_pas rom3.pas
% rom3_pas
MMMCMXLVII
% cat rom4.pas
(* Equivalent Pascal version of the    *)
(*   original Fortran program          *)
(* Shows 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.
% pc -o rom4_pas rom4.pas
% rom4_pas
MMMCMXLVII


4. C version of the original Fortran program. Here is the C version. Notice that it would not be possible to write this program directly in the Java language, because Java has no goto statement. Source file is: rom.c.

C Program With GOTOs
% cat rom.c
/* C version of program */
#include <stdio.h>
int rom, i, l, n, k;
char s[15];
/* initial blank below due to 
 *   C's 0-based arrays */
char r[8]= " ivxlcdm";
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);
}
% cc -o rom rom.c
% rom
 mmmcmxlvii


5. "Mystery" Program in C. Here is an even more obscure program written in C, a "mystery" program completely different from the previous ones (and more complicated). Source file is: mystery.c.

"Mystery" C Program With GOTOs
% cat mystery.c
#include <stdio.h>
#define N 100
int k[N+1];

int main() {
     int n = 9973, i, j, y, c, K, P;
     int f = 0, g = 1, h = 1;
     int M, I, p, q, t, r;

     i = 1;
L1:  if (i > N) goto L2;
     n = (n*15551) % 19993;
     k[i] = n;
     i++;
     goto L1;
L2:  i = 2;
L3:  if (i > N) goto L16;
     y = k[i];
     K = k[i]; P = i-1;
L4:  if (h - 1 >= P) goto L5;
     f = g;
     g = h;
     h = f + g;
     goto L4;
L5:  M = h - 1 - P;
     I = g - M;
     p = f;
     q = g - f;
L6:  if ( I > 0 && K <= k[I]) goto L8;
     if (p != 1) goto L7;
     r = -I;
     goto L12;
L7:  I = I + q;
     p = p - q;
     q = q - p;
     goto L6;
L8:  if (K != k[I]) goto L9;
     r = I;
     goto L12;
L9:  if (K >= k[I]) goto L11;
     if (q != 0) goto L10;
     r = -(I - 1);
     goto L12;
     /* continue in next column */
L10: I = I - q;
     t = p;
     p = q;
     q = t - q;
     goto L6;
L11: goto L6;
L12: c = r;
     if (c >= 0) goto L13;
     c = -c;
L13: j = i - 1;
L14: if (j < c + 1) goto L15;
     k[j+1] = k[j];
     j--;
     goto L14;
L15: k[c+1] = y;
     i++;
     goto L3;
L16: P = N;
     printf("  ");
     i = 1;
L17: if (i > 10) goto L18;
     printf("%i\t", i);
     i++;
     goto L17;
L18: printf("\n +");
     i = 1;
L19: if (i > 9) goto L20;
     printf("--------");
     i++;
     goto L19;
L20: printf("\n");
     i = 1;
L21: if (i > P) goto L24;
     if (i%10 != 1) goto L22;
     printf("%i|", i/10);
L22: printf("%i\t", k[i]);
     if (i%10 != 0) goto L23;
     printf("\n");
L23: i++;
     goto L21;
L24: printf("\n\n");
     return 0;
}
% cc -o mystery mystery.c
% mystery
Output of "Mystery" C Program
  1     2       3       4       5       6       7       8       9       10
 +------------------------------------------------------------------------
0|182   532     814     865     914     969     1452    1655    1668    2092
1|2407  2432    2435    2479    2736    2864    2945    3078    3222    4042
2|4081  4197    4361    4416    4422    4425    4531    4968    5203    5207
3|5288  5574    5589    5620    5849    5914    5949    6249    6959    6990
4|7217  7955    8147    8357    8461    8623    8804    9642    9893    10142
5|10165 10328   10395   10595   10858   10894   11257   11269   11514   11619
6|11630 11873   11905   12136   12200   12219   12350   12862   13009   13269
7|13278 13458   13653   13725   13785   14190   15235   15344   16023   16319
8|16408 16899   17016   17254   17262   17293   17593   18082   18349   18459
9|18467 18584   18627   18892   18933   19143   19468   19542   19901   19936


6. Recitation work: For this recitation, you are to practice translating two programs with GOTOs into equivalent programs without GOTOs. Specifically, you are to translate the two C programs in sections 4 and 5 above into Java programs using one of the two strategies illustrated in sections 2 and 3 above. (You do not need complicated Java -- everything can even be in a single main function.) For the initial work, you should not translate to a structured version of the program even if you could figure out how to make it structured. In other words, you should initially not try to uncover the loops within the program and translate them to actual loops. Discovering such loops is for the second part of this recitation. It is not too hard to do in this example, but it is impossible to do in the general case.


7. What you should submit: Refer to the submissions directions and to deadlines at the top of this page. The text file that you submit should first have Your Name, the Course Number, and the Recitation Number. The rest of the file should have the following in it, in the order below, and clearly labeled, including at the beginning the appropriate item letters: a, b, c, etc.

 Contents of submission for Recitation 1:

Last Name, First Name; Course Number; Recitation Number.

a. Java source program that translates the C program in Section 4 above: (.java file). This program should have the organization of one of the programs on the right in Sections 2 or 3. Specifically, it should have a single while and inside that a single switch statement. It should have no goto statements.

b. Log of a run of the program in a, including the output.

c. Java source program that translates the C program in Section 5 above (the "mystery" program). The same remarks apply as for item a.

d. Log of a run of the program in c, including the output.

e. Java source program that recovers the hidden loops and the structure of the C program in Section 4. This recitation has not taught any specific techniques for this, but the presence of loops and if statements should be fairly clear.

f. Log of a run of the program in e, including the output.

    [Parts g, h, i, j, and k below are harder, something of interest to better students. Please do not tell anyone the answers if you figure them out. I do not plan to provide answers to these parts.]

g. Java source program that recovers the hidden loops and the structure of the C program in Section 5. Again, loops should be fairly obvious, but this part is a little more complex because it is longer. There is also a fairly clear function call. [Hint: the program in Section 5 is sufficiently complicated that you should first convert it to a C program with recovered control structures (and without GOTOs), and you should do this carefully in small steps. You have a C program that compiles and has a known output, so at each small step verify that you still have a C program that compiles and has the same output.]

h. Log of a run of the program in g, including the output.

i. The algorithm used in the first program in this recitation (to translate to Roman numerals) looks intricate because it has been derived and altered from a simpler program: Roman numeral program in Java. The "simpler" program is shorter and easier to understand, but it uses two arrays of data: of ints and of Strings, whereas the other program just uses one short string. At the core these programs use a simple algorithm construction method that is often taught in a course such as CS3343 (Algorithms). (An example of a general algorithm construction method is "divide-and-conquer", but that is not the correct answer here.) What is this general method?

j. (Harder) The algorithm used in the second program in this recitation (the "mystery" program) is also intricate, but its general approach is fairly standard. What is this algorithm doing and roughly how is it working?

k. (Very hard) At the heart of the the second program is a loop (or actually a function call) in the original. This loop (or function call) by itself is a strange algorithm that is almost certainly unfamiliar to every student. (It is my all-time favorite algorithm!) Try to figure out how it is working. If you do figure it out, send me a personal email (wagner@cs.utsa.edu). If anyone figures it out, I will give a prize (a book) to the first one.


9. 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. However, eliminating the GOTOs in this way will not make the structure of the resulting program easier to understand than the original program.


Revision date: 2003-12-20. (Please use ISO 8601, the International Standard.)