Path: utzoo!utgpu!news-server.csri.toronto.edu!qucdn!spraggej Organization: Queen's University at Kingston Date: Saturday, 19 Jan 1991 01:04:03 EST From: John G. Spragge Message-ID: <91019.010403SPRAGGEJ@QUCDN.QueensU.CA> Newsgroups: comp.lang.pascal Subject: Re: Gotos are ok (Was Re: IMPLEMENT GOTO ACROSS MODULES IN TURBO PASCA References: <1991Jan10.122222.1013@uwasa.fi> <1991Jan16.005523.28337@syacus.acus.oz> <7612@plains.NoDak.edu> <2802@oucsace.cs.OHIOU.EDU> I have never had to use the goto statement in Pascal and, like Professor Salmi, I have written my share of Pascal code. However, I believe that refraining from the use of gotos can be a useful discipline. The issue of gotos is debated so fervently because it turns on the question of what place these minor disciplines have in programming. Because computers are so flexible, I find that disciplined programming is an essential method for controlling complexity. I program according to a very rigid set of rules: PROCEDURE Demonstrated { this is a demonstration procedure } ( VAR a : INTEGER { comment describes the parameter } ); VAR n : REAL; { each declaration is commented } BEGIN { demonstrated } n := FLOAT (a); { must convert to a real } a := ROUND (n * 72) END; { demonstrated } All of these habits can be defended as improving the readability of the program in some way (having the comments always end in the same column makes it easier to pick out the commented lines, commenting the variables makes it possible to remember what they do, and so on); but what the discipline of staying in the format principally accomplishes is to make me stop and think periodically about what I'm doing. This in turn allows me to develop the clearest possible structures for coding. I developed the habit of never going past column 72 on an IBM system where that Pascal compiler required the programmer to set margins, but I found it a useful discipline, both because it was a good way of making sure I could always print my listings, and also because it discouraged me from using loops that were nested to deeply. Students are often told to ensure their procedures fit on a page; that helps make sure that no procedure can get overly complex. Consider an example used earlier: In article <2802@oucsace.cs.OHIOU.EDU>, tswingle@oucsace.cs.OHIOU.EDU (Tom Swingle) says: <<<<< I reformatted the quote so it would fit on my screen >>>>>> > >Here is a situation where I found a goto to be the neatest, easiest way to >solve a problem. I had a procedure with nested repeat loops, at least two >deep. I wanted to break out of the procedure if an error occurred, but first >there were things that had to be done (restoring screen, closing files, etc.) >before I could leave the procedure. These things needed to be done with >either normal or abnormal termination of the procedure, and its code was >several lines long. Normally, it would be done at the end of the procedure >(logical, right?) but if I needed to get out immediately, I needed a way to >execute this code and leave. Here's a skeleton of the procedure: <<<< Mr. Swingle's skeleton deleted. Here's my approach to the problem >>>> VAR cc : INTEGER; { Control circuit counter } inv : BOOLEAN; { invalid input flag } rec : datatype; { record type buffer } BEGIN { procedure } REPEAT { outer repeat loop } { any set of statements you like } REPEAT { next repeat loop } { any other statement you like } cc := 0; inv := false; WHILE (cc < 3) AND NOT inv DO { loop/select command } BEGIN cc := cc + 1; { next command } CASE cc OF 1 : BEGIN getfirstfield (rec); inv := rec.first < 0 END; 2 : BEGIN getsecondfield (rec); inv := rec.second > 10000 END; 3 : BEGIN getthirdfield (rec); inv := (rec.third > 100) AND (rec.third < -100); END; END { Case } END { While } UNTIL inv OR (innerloopend) UNTIL inv OR innerloopend; { cleanup code goes in here } END; { procedure } I can foresee three objections to this code. Let me take them in order. 1) That code isn't efficient. In my experience, almost all users prefer a program that takes five minutes more to run over one that locks up, bombs, or produces erroneous output once in a thousand runs. Of all uses for run time, taking time to check your errors and implement a clear structure is about the most easily forgiven by the user. If the program isn't fast enough, find a better algorithm. If you can't do that, recode the 1% to 10% of the program where speed is critical in assembler. But don't use "efficiency" as an excuse to put in a goto. 2) It uses an extra variable. Control and state variables aren't "extra". They're a much a part of the program as data buffers and counters. And please note that using a goto does NOT save you a declaration: you have to declare the label you jump to. 3) It isn't really clear. If you don't like this technique, you don't have to use it. I use it to group repetitive tests for the validity of input together rather than using a bunch of if-then constructs. It has three advantages: it's succinct; you can tell clearly what's being done and in what order; the test that gets you out of the input process is done in one place; it leaves the close code knowing that invalid input was received, and in the CC variable, you get a record of exactly what input was wrong. It's useful for most of my purposes. The point is, you have no "obligation" to avoid gotos. But for every goto you include, you should probably consider what other technique you could have used to accomplish the same thing, and know why you chose to us the goto instead. Programming discipline is not about keeping rules; the rules are there so that when you bump into them you have to think about what you're doing. disclaimer: Queen's University merely supplies me with computer services, and they are responsible for neither my opinions or my ignorance. John G. Spragge