|
CS 3721, Spring 2004 |
Recitation 1 must be submitted
following directions at: submissions on or before
|
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.
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
|
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
|
| 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
|
| "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 | |
|
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.
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. |