CS 3721
Programming Languages  
Spring 2013
Recitation 1.   No GOTO
Week 1: Jan 14-18

Submit following directions at: submissions and rules at: rules. Deadlines are:
  • 2013-01-22  23:59:59 (that's Tue, 22 Jan 2013, 11:59:59 pm) for full credit.
  • 2013-01-25  23:59:59 (that's Fri, 25 Jan 2013, 11:59:59 pm) for 75% credit.

List of names of students submitting, final list: Rec1 Submissions.


1. Overview: This recitation is concerned with the goto statement, which is a part of most languages, with the notable exception of Java. More than 40 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 in 1968 by E. Dijkstra, a Dutch computer scientist: Dijkstra's original article. For these and other reasons 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.

Dijkstra later made clear that the goto itself was not necessarily bad if it was used for an orderly termination of a loop (as with a break statement).

The Java language has no goto, but it has a break statement to break out of a loop (as with C and C++), and the more complicated labeled break which lets one break out of multiple nested loops. Java also has a continue statement which lets one skip the remainder of a loop and start over at the header.

Some sophisticated Java books (such as those by Horstmann) never use break or continue and discourage its use. Horstmann also never uses the Java switch statement, mainly because it's too error prone (if you leave off a break from a switch clause, execution of that clause will fall through and execute the next clause).

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. It's also possible to do without break and continue, but we won't discuss that further here.

    • Any program written using gotos can be rewritten without gotos
      so that it carries out exactly the same computation.

    • 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, and 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. The Pascal "case" statement is like a "switch" statement in C/C++/Java. Each case (1-18) corresponds to a line number. After executing a case, the program either increments LINE to the next line number (in case control in the original program went on to the next line), 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 or simpler or easier to understand 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 (preferably) or C programs (C without gotos) 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. The second part of this recitation has you discovering such loops.


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 email submission for this recitation:

Last Name, First Name; CS 3723; Recitation Number.
Last Name, First Name (of second person, if two are working together).

  1. Java or C source program that translates the C program in Section 4 above: (.java or .c 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. (Of course you may use an extended if-then-else instead of a switch.)

  2. Log of a run of the program in 1, including the output.

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

  4. Log of a run of the program in 3, including the output.

  5. Java or C 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.

  6. Log of a run of the program in 5, including the output.

  7. (Much Harder) For this part, you are to write a Java or C source program that recovers the hidden loops and the structure of the C program in Section 5 (the "mystery" program). This is much harder than the example in Section 4. This example is partly to demonstrate just how difficult it can be to deal with gotos. If you don't eliminate all gotos, you should submit a program representing how far you got.

    [Hint: One way to proceed is to start with the given program that compiles (hopefully) and has a given output. At each stage, you can make small changes, recovering first the most obvious loops one at a time, and repeatedly checking that it still compiles with the same output. If you think about it properly, you'll see that at the core there is a function call and a function definition.]

  8. (Also hard) The algorithm used in the program in section 5 (the "mystery" program) is intricate, but its general approach is fairly straightforward. Roughly what is it doing?


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: 2012-12-30. (Please use ISO 8601, the International Standard.)