<<

with go to Statements

DONALD E. KNUTH , Stanford, California 9~S05

A consideration of several different examples sheds new light on the problem of ereat- ing reliable, well-structured programs that behave efficiently. This study focuses largely on two issues: (a) improved syntax for iterations and error exits, making it possible to write a larger class of programs clearly and efficiently without go to - ments; (b) a methodology of program design, beginning with readable and correct, but possibly inefficient programs that are systematically transformed if necessary into efficient and correct, but possibly less readable code. The discussion brings out op- posing points of view about whether or not go to statements should be abolished; some merit is found on both sides of this question. Fina!ly, an attempt is made to define the true nature of structured programming, and to recommend fruitful direc- tions for further study. Keywords and phrases: structured programming, go to statements, language design, event indicators, recursion, Boolean variables, iteration, optimization of programs, program transformations, program manipulation systems searching, Quieksort, efficiency CR categories: 4.0, 4.10, 4.20, 5.20, 5.5, 6.1 (5.23, 5.24, 5.25, 5.27)

You may go when you will go, And I will stay behind. --Edna St. Vincent Millay [66] Most likely you go your way and I'll go mine. --Song title by Bob Dylan [33] Do you suffer from painful elimination? --Advertisement, J. B. Williams Co.

INTRODUCTION change your life. The reasons for this revolu- tion and its future prospects have been aptly A revolution is taking place in the way we described by E. W. Dijkstra in his 1972 Tur- write programs and teach programming, be- ing Award Lecture, "The Humble Program- cause we are beginning to understand the mer" [27l. associated mental processes more deeply. It As we experience this revolution, each of is impossible to read the recent book Struc- us naturally is developing strong feelings one tured programming [17; 55] without having it way or the other, as we agree or disagree This research was supported in part by the Na- with the revolutionary leaders. I must admit tional Science Foundation under grant number GJ 36473X, and by IBM Corporation. to being a nomhumble , egotisti-

Copyright (~) 1974, Association for Machinery, Inc. General permission to republish, but not for profit, all or part of this material is granted, provided that ACM's copyright notice is given and that reference is made to this publication, to its date of issue, and to the fact that reprint- ing privileges were granted by permission of the Association for Computing Machinery.

Computing Surveys, V?L 6, No. 4, Dee,ember 1974 262 , Donald E. Knuth

CONTENTS eal enough to believe that my own opinions of the current treads are not a waste of the reader's time. Therefore I want to express in this article several iof the things that struck me most forcefully as I have been thinking about structured programming during the last year; several of my blind spots were re- INTRODUCTION moved as I ivas learning these things, and I 1. ELIMINATION OF so to STATEMENTS hope I can convey some of my excitement to Historical Background A Searching Example the reader. Hardly any of the ideas I will Efficiency discuss are my own; they are nearly all the Error Exits work of others, but perhaps I may be pre- Subscript Checking Hash Coding senting them in a new light. I write this Text Scanning article in the first person to emphasize the A Confession fact that what I'm saying is just one man's Tree Searching Systematic Elimination opinion; I don't expect to persuade everyone Event Indicators that my present views are correct. Comparison of Features Before beginning a more technical discus- Simple Iterations 2. INTRODUCTION OF so to STATEMENTS sion. I should confess that the title of this Recursion Elimination article was chosen primarily to generate Program Manipulation Systems attention. There are doubtless some readers Reeursion vs. Iteration Boolean Variable Elimination who are convinced that abolition of go to statements is merely a fad. and they may see Quicksort : A Digression Axiomatics of Jumps this title and think, "Aha! Knuth is rehabili- Reduction of Complication tating the go to statement, and we can go 3. CONCLUSIONS back to our old ways of programming Structured Programming With go to Statements again." Another class of readers will see the Efficiency heretical title and think, "When are die- The Future hards like Knuth going to get with it?" I ACKNOWLEDGMENTS APPENDIX hope that both classes of people will read on BIBLIOGRAPHY and discover that what I am really doing is striving for a reasonably well balanced view- point about the proper role of go to state- ments. I argue for the elimination of go to's in certain cases, and for their introduction in others. I believe that by presenting such a view I am not in fact disagreeing sharply with Dijkstra's ideas, since he recently wrote the following: "Please don't fall into the trap of believing that I am terribly dogmatical about [the go to statement]. I have the uncomfortable feeling that others are making a religion out of it, as if the conceptual problems of programming could be solved by a single trick, by a simple form of coding discipline!" [29]. In other words, it, seems that fanatical advocates of the New Pro- gramming are going overboard in their strict enforcement of morality and purity in programs. Sooner or later people are going to find that their beautifully-structured

Computing Surveys, Vol. 6, No. 4, December 1974 Structured Programming with go to Sta~ment~ * 263 ! programs are running at only half the speed we also hear complaints about floating-point --or worse--of the dirty old programs they calculations, global variables, semaphores, used to write, and they will mistakenly blame pointer variables, and even assignment the structure instead of recognizing what is statements. Soon we might be restricted to probably the real culprit--the system over- only a dozen or so programs that are suffi- head caused by typical implementa- ciently simple to be allowable; then we will tion of Boolean variables and procedure calls. be almost certain that these programs Then we'll have an unfortunate counter- cannot lead us into any trouble, but of revolution, something like the current rejec- course we won't be able to solve many tion of the "New Mathematics" in reaction problems. to its over-zealous reforms. In the mathematical ease, we know what It may be helpful to consider a further happened: The intuitionists taught the other analogy with mathematics. In 1904, Bert- mathematicians a great deal about deductive rand Russell published his famous paradox methods, while the other mathematicians about the set of all sets which aren't mem- cleaned up the classical methods and even- bers of themselves. This antinomy shook the tually "won" the battle. And a revolution foundations of classical mathematical rea- did, in fact, take place. In the computer soning, since it apparently brought very science case, I imagine that a similar thing simple and ordinary deductive methods into will eventually happen: purists will point the question. The ensuing crisis led to the rise way to clean constructions, and others will of "intuitionist logic", a school of thought find ways to purify their use of floating-point championed especially by the Dutch mathe- arithmetic, pointer variables, assignments, matician, L. E. J. Brouwer; intuitionism etc., so that these classical tools can be used abandoned all deductions that were based on with comparative safety. questionable nonconstructive ideas. For a Of course all analogies break down, includ- while it appeared that intuitionist logic ing this one, especially since I'm not yet would cause a revolution in mathematics. conceited enough to compare myself to But the new approach angered David Hil- David Hilbert. But I think it's an amusing bert, who was perhaps the leading mathema- coincidence that the present programming tician of the time; Hilbert said that "For- revolution is being led by another Dutchman bidding a mathematician to make use of the (although he doesn't have extremist views principle of the excluded middle is like corresponding to Brouwer's); and I do forbidding an astronomer his telescope or a consider assignment statements and pointer boxer the use of his fists." He characterized variables to be among 's the intuitionist approach as seeking "to "most valuable treasures!'. save mathematics by throwing overboard At the present time I think we are on the all that is troublesome .... They would chop verge of discovering at last what program- up and mangle the science. If we would ming languages should really be like. I look follow such a reform as they suggest, we forward to seeing many responsible experi- could run the risk of losing a great part of our ments with language design during the next most valuable treasures" [80, pp. 98-99, few years; and my dream is that by 1984 we 148-150, 154-157, 184-185, 268-270]. will see a consensus developing for a really Something a little like this is happening good (or, more likely, in computer science. In the late 1960's we a coherent family of languages). Further- witnessed a "", which many more, I'm guessing that people will become people thought was paradoxical because so disenchanted with the languages they are programming was supposed to be so easy. now using--even COBOL and -- As a result of the crisis, people are now be- that this new language, UTOPXA84, will have ginning to renounce every feature of pro- a chance to take over. At present we are far gramming that can be considered guilty by from that goal, yet there are indications virtue of its association with difficulties. Not that such a language is very slowly taking only go to statements are being questioned; shape.

Computing Surveys, Vol. 6, No. 4, December 1974 264 • Donald E. Knuth

Will UTOPIA 84, or perhaps we should call documentation of a program, instead of using it NEWSPEAK, contain go to statements? At flow charts... Then I would code the pro- gram in from the outline. the moment, unfortunately, there isn't even Everyone liked these outlines better than a consensus about this apparently trivial the flow charts I had drawn before, which issue, and we had better not be hung up on were not very neat--my flow charts had been the question too much longer since there are nick-named "balloon-o-grams". only ten years left. He reported that this method made programs I will try in what follows to give a reason- easier to plan, to modify and to check out. ably comprehensive survey of the go to When I met Schorre in 1963, he told me of controversy, arguing both pro and con, with- his radical ideas, and I didn't believe they out taking a strong stand one way or the would work. In fact, I suspected that it was other until the discussion is nearly complete. really his rationalization for not finding an In order to illustrate different uses of go to easy way to put labels and go to statements statements, I will discuss many example into his META-II subset of ALGOL [84], a programs, some of which tend to negate the language which I liked very much except for conclusions we might draw from the others. this omission. In 1964 I challenged him to There are two reasons why I have chosen to write a program for the eight-queens prob- present the material in this apparently lem without using go to statements, and he vacillating manner. First, since I have the responded with a program using recursive opportunity to choose all the examples, I procedures and Boolean variables, very much don't think it's fair to load the dice by select- like the program later published independ- ing only program fragments which favor one ently by Wirth [96]. side of the argument. Second, and perhaps I was still not convinced that all go to most important, I tried this approach when I statements could or should be done away lectured on the subject at UCLA in Feb- with, although I fully subscribed to Peter ruary, 1974, and it worked beautifully: Naur's observations which had appeared nearly everybody in the audience had the about the same time [73]. Since Naur's illusion that I was largely supporting his or comments were the first published remarks her views, regardless of what those views about harmful go to's, it is instructive to were ! quote some of them here: If you look carefully you will find that surpris- ingly often a go to statement which looks back 1. ELIMINATION OF go to STATEMENTS really is a concealed for statement. And you will be pleased to find how the clarity of the improves when you insert the for Historical Background clause where it belongs.... If the purpose [of At the IFIP Congress in 1971 I had the a programming course] is to teach ALGOLpro- pleasure of meeting Dr. Eiichi Goto of gramming, the use of flow will do Japan, who cheerfully complained that he more harm than good, in my opinion. was always being eliminated. Here is the The next year we find George Forsythe history of the subject, as far as I have been also purging go to statements from - able to trace it. rithms submitted to Communications of the The first programmer who systematically ACM (cf. [53]). Incidentally. the second began to avoid all labels and go to state- example program at the end of the original ments was perhaps D. V. Schorre, then of ALGOL 60 report [72] contains four go to UCLA. He has written the following account statements, to labels named AA, BB, CC, of his early experiences [85]: and DD, so it is clear that the advantages of Since the summer of 1960, I have been writing ALGOL'S control structures weren't fully programs in outline form, using conventions of perceived in 1960. indentation to indicate the flow of control. I In 1965, Edsger Dijkstra published the have never found it necessary to take excep- following instructive remarks [21]: tion to these conventions by using go state- ments. I used to keep these outlines as original Two programming department managers from

Computing Surveys, Vol. 6, No. 4, December 1974 Structured Programming with go to Statements • 265

different countries and different backgrounds By 1967, the entire XPL compiler had --the one mainly scientific, the other mainly been written by McKeeman, Homing, and commercial--have communicated to me, in- dependently of each other and on their own Wortman, using go to :only once ([65], pp. initiative, their observation that the quality 365-458; the go to is on page 385). In 1971, of their was inversely propor- Christopher Strachey [87] reported that "It tional to the density of statements in is my aim to write programs with no labels. their programs .... I have done various pro- I am doing quite well. I have got the operat- gramming experiments.., in modified ver- sions of ALGOL 60 in which the goto statement ing system down to 5 labels and I am plan- was abolished .... The latter versions were ning to write a compiler with no labels at more difficult to make: we are so familiar with all." In 1972, an entire session of the ACM the jump order that it requires some effort to National Conference was devoted to the forget it! In all cases tried, however, the subject [44; 60; 100]. The December, 1973, program without the goto statement turned out to be shorter and more lucid. issue of Datamation featured five articles about structured programming and elimina- A few months later, at the ACM Pro- tion of go to's [3; 13; 32; 64; 67]. Thus, it is gramming Languages and Pragmatics Con- clear that sentiments against go to state- ference, Peter Landin put it this way [59]: ments have been building up. In fact, the There is a game sometimes played with ALGOL discussion has apparently caused some 60 programs--rewriting them so as to avoid people to feel threatened; Dijkstra once told using go to statements. It is part of a more me that he actually received '% torrent of embracing game--reducing the extent to abusive letters" after publication of his which the program conveys its information by explicit sequencing .... The game's signifi- article. cance lies in that it frequently produces a The tide of opinion first hit me personally more "transparent" program---easier to in 1969, when I was teaching an introductory understand, debug, modify, and incorporate programming course for the first time. I into a larger program. remember feeling frustrated on several reinforced this opinion at the occasions, at not seeing how to write pro- same meeting [74, p. 179]. grams in the new style; I would run to Bob The next chapter in the story is what many Floyd's office asking for help, and he usually people regard as the first, because it made the showed me what to do. This was the genesis most waves. Dijkstra submitted a short of our article [52] in which we presented two article to Communications of the ACM, de- types of programs which did not submit voted entirely to a discussion of go to state- gracefully to the new prohibition. We found meats. In order to speed publication, the that there was no way to implement certain editor decided to publish Dijkstra's article simple constructions with while and condi- as a letter, and to supply a new title, "Go to tional gtatemeats substituted for go to's, statement ". This note unless extra computation was specified. [23] rapidly became well-known; it expressed During the last few years several languages Dijkstra's conviction that go to's "should have appeared in which the designers be abolished from all 'higher level' program- proudly announced that they have abolished ming languages (i.e., everything except, the go to statement. Perhaps the most perhaps, plain ) .... The go to prominent of these is Brass [98], which statement as it stands is just too primitive; originally replaced go to's by eight so-called it is too much an invitation to make a mess of "escape" statements. And the eight weren't one's program." He encouraged looking for even enough; the authors wrote, "Our alternative constructions which may be mistake was in assuming that there is no necessary to satisfy all needs. Dijkstra also need for a once the go to is removed," recalled that Heinz Zemanek had expressed and they later [99, 100] added a new state- doubts about go to statements as early as ment "leave (label) with (expression)" 1959; and that Peter Landin, Christopher which goes to the place after the statement Strachey, . A. R. Hoare and others had identified by the (label). Other go to-less been of some influence on his thinking. languages for systems programming have

Computing Surveys, VoL 6, No. 4, December 1974 266 • Donald E. Knuth similarly introduced other statements which more computation an.d aren't really more provide "equally powerful" alternative ways perspicuous. Therefore, this example has to jump. been widely quoted in defense of the go to In other words, it seems that there is wide- statement, and it is appropriate to scrutinize spread agreement that go to statements are the problem carefully. harmful, yet programmers and language Let's suppose that we've been forbidden designers still feel the need for some euphe- to use go to statements, and that we want mism that "goes to" without saying go to. to do precisely the computation specified in Example 1 (using the obvious expansion of A Searching •×ampM such a for statement into assignments and What are the reasons for this? In [52], Floyd a while iteration). If this means not only and I gave the following example of a typical that we want the same results, but also that program for which the ordinary capabilities we want to do the same operations in the of while and if statements are inadequate. same order, the mission is impossible. But if Let's suppose that we want to search a table we are allowed to weaken the conditions A[1] ... A[m] of distinct values, in order to just slightly, so that a relation can be tested find where a given value x appears; if x is not twice in succession (assuming that it will present in the table, we want to insert it as yield the same result each time, i.e., that it an additional entry. Let's suppose further has no side-effects), we can solve the problem that there is another array B, where B[,] as follows: equals the number of times we have searched for the value A[i]. We might solve such a Example la: problem as follows: i:=1; while i < m and A[i] # x do i :-- i+1; Example 1: ifi > m then ra := i; A[i] := x; B[i] ::= 0 fi; B[i] := B[i]+I; for i := 1 step 1 until m do. if A[i] = x then go to found fi; The and operation used here stands for not found: i := re+l; m := i; McCarthy's sequential conjunction operator A[i] := x; B[i] := 0; found: B[i] := B[i]+I; [62, p. 185]; i.e., "p and q" means "if p then q else false fl", so that q is not evalu- (In the present article I shall use an ad hoc ated when p is false. Example la will do programming language that is very similar exactly the same sequence of computations to ALGOL60, with one exception: the symbol as Example 1, except for one extra compari- fi is required as a closing bracket for all if son of i with m (and occasionally one less statements, so that begin and end aren't computation of m+ 1). If the iteration in this needed between then and else. I don't while loop is performed a large number of really like the looks of fi at the moment; but times, the extra comparison has a negligible it is short, performs a useful function, and effect on the running time. connotes finality, so I'm confidently hoping Thus, we can live without the go to in that I'll get used to it. has re- Example 1. But Example la is slightly less marked that tl is a perfect example of a readable, in my opinion, as well as slightly cryptic notation that can make program- slower; so it isn't clear what we have gained. ming unnecessarily complicated for begin- Furthermore, if we had made Example 1 ners; yet I'm more comfortable with fi every more complicated, the trick of going to Ex- time I write it. I still balk at spelling other ample la would no longer work. For ex- symbols backwards, and so do most of ample, suppose we had inserted another the people I know; a student's paper con- statement into the , just before the taining the code fragment "esae; comment if clause; then the relations i _< m and bletch tnemmoc;" is a typical reaction to A[i] -- x wouldn't have been tested consecu- this trend !) tively, and we couldn't in general have com- There are ways to express Example 1 bined them with and. without go to statements, but they require John Cooke told me an instructive story

Computing Surveys, Vol. 6, No. 4, December 1974 Structured Programming with go to ,Sta~nezd~ - 267 L relating to Example 1 and to the design of programs are translated by a typical "90 % languages. Some PL/I programmers were efficient compiler" wi~h bounds-checking asked to do the stated search problem with- suppressed, the corresponding run-time out using jumps, and they came up with figures are respectively about 14n + 5 and essentially the following two solutions: lln + 21. (The appendix to this paper explains the ground rules for these calcula- tions.) Under the first assumption we save a) DO I - 1 to M WHILE A(I) -~ ffi X; about 33 % of the run-time, and under the END; IF I > M THEN second assumption we save about 21%, so DO; M z I; A(I) = X; B(I) ffi 0; END; B(I) ffi B(I) + I; in both cases the elimination of the go to b) FOUND = 0; has also eliminated some of the running DO I - i TO M WHILE FOUND = 0; IF A(I) - X THEN FOUND = i; time. END; IF FOUND ffi 0 THEN DO; M - I; A(I) = X; B(I) ffi 0; END; Efficiency B(I) - B(I) ffi 1; The ratio of running times (about 6 to 4 in the first case when n is large) is rather sur- Solution (a) is best, but since it involves a prising to people who haven't studied pro- null iteration (with no explicit statements gram behavior carefully. Example 2 doesn't being iterated) most people came up with look that much more efficient, but it is. Solution (b). The instructive point is that Experience has shown (see [46], [51]) that Solution (b) doesn't work; there is a serious most of the running time in non-IO-bound bug which caused great puzzlement before programs is concentrated in about 3 % of the the reason was found. Can the reader source text. We often see a short inner loop spot the difficulty? (The answer appears on whose speed governs the overall program page 298.) speed to a remarkable degree; speeding up As I've said, Example 1 has often been the inner loop by 10 % speeds up everything used to defend the go to statement. Un- by almost 10 %. And if the inner loop has 10 fortunately, however, the example is totally instructions, a moment's thought will usually unconvincing in spite of the arguments I've cut it to 9 or fewer. stated so far, because the method in Example My own has of course 1 is almost never a good way to search an changed during the last decade, according to array for x ! The following modification to the the trends of the times (e.g., I'm not quite so data structure makes the algorithm much tricky anymore, and I use fewer go to's), better: but the major change in my style has been Example 2: due to this inner loop phenomenon. I now look with an extremely jaundiced eye at A[mq-1] := x; i := 1; every operation in a critical inner loop, seek- while A[i] ~ ~c do i := i+1; ing to modify my program and data struc- ifi > m then m := i; B[i] := 1; ture (as in the change from Example 1 to else B[i] := B[i]+I fi; Example 2) so that some of the operations Example 2 beats Example 1 because it can be eliminated. The reasons for this ap- makes the inner loop considerably faster. If proach are that: a) it doesn't take long, since we assume that the programs have been the inner loop is short; b) the payoff is real; handcoded in assembly language, so that the and c) I can then afford to be less efficient values of i, m, and x are kept in registers, in the other parts of my programs, which and if we let n be the final value of i at the therefore are more readable and more easily end of the program, Example 1 will make written and debugged. Tools are being 6n + 10 (+3 if not found) references to developed to make this critical-loop identifi- memory for data and instructions on a cation job easy (see for example [46] and typical computer, while the second program [82]). will make only 4n + 14 (+6'if not found).• Thus. if I hadn't seen how to remove one If, on the other hand, we assume that these of the operations from the loop in Example I

Computing Surveys, V61. 6, No. 4, De.~mber I~4 268 • Donald E. Knuth by changing to Example 2. I would probably the same viewpoint should prevail in soft- (at least) have made the for loop run from ware engineering~ Of course I wouldn't m to 1 instead of from 1 to m, since it's bother making such optimizations on a one- usually easier to test for zero than to com- shot job, but when it's a question of prepar- pare with m. And if Example 2 were really ing quality programs, I don't want to re- critical, I would improve on it still more by strict myself to tools that deny me such "doubling it up" so that the machine code efficiencies. would be essentially as follows. There is no doubt that the grail of effi- Example 2a: ciency leads to abuse. Programmers waste enormous amounts of time thinking about, A[m+l] := x; i := 1; go to test; or worrying about, the speed of noncritical loop: i := i+2; parts of their programs, and these attempts test: if A[i] = x then go to found fi; at efficiency actually have a strong negative if A[i+I] ~ x then go to loop fi; i := i+1; impact when debugging and maintenance are found: ifi > m then m := i; B[i] := 1; considered. We should forget about else B[i] := B[i]+I fi; efficiencies, say about 97% of the time: pre- Here the loop variable i increases by 2 on mature optimization is the root of all evil. each iteration, so we need to do that opera- Yet we should not pass up our opportuni- tion only half as often as before; the rest of ties in that critical 3 %. A good programmer the code in the loop has essentially been will not be lulled into complacency by such duplicated to make this work. The running reasoning, he will be wise to look carefully time has now been reduced to about 3.5n + at the critical code; but only after that code 14.5 or 8.5n + 23.5 under our respective has been identified. It is often a mistake to assumptions--again this is a noticeable make a priori judgments about what parts saving in the overall running speed, if, say, of a program are really critical, since the the average value of n is about 20, and if universal experience of programmers who this search routine is performed a million or have been using measurement tools has been so times in the overall program. Such loop- that their intuitive guesses fail. After work- optimizations are not difficult to learn and, ing with such tools for seven years, I've be- as I have said, they are appropriate in just come convinced that all written a small part of a program, yet they very from now on should be designed to provide often yield substantial savings. (Of course if all programmers with feedback indicating we want to improve on Example 2a still what parts of their programs are costing more, especially for large m, we'll use a more the most; indeed, this feedback should be sophisticated search technique; but let's supplied automatically unless it has been ignore that issue, at the moment, since I specificMly turned off. want to illustrate loop optimization in gen- After a programmer knows which parts of eral, not searching in particular.) his routines are really important, a trans- The improvement in speed from Example formation like doubling up of loops will be 2 to Example 2a is only about 12%, and worthwhile. Note that this transformation many people would pronounce that insig- introduces go to statements--and so do nificant. The conventional wisdom shared several other loop optimizations; I will re- by many of today's software engineers calls turn to this point later. Meanwhile I have for ignoring efficiency in the small; but I to admit that the presence of go to state- believe this is simply an overreaction to the ments in Example 2a has a negative as well abuses they see being practiced by penny- as a positive effect on efficiency; a non- wise-and-pound-foolish programmers, who optimizing compiler will tend to produce can't debug or maintain their "optimized" awkward code, since the contents of regis- programs. In established engineering dis- ters can't be assumed known when a label is ciplines a 12 % improvement, easily obtained, passed. When I computed the running times is never considered marginal; and I believe cited above by looking at a typical compiler's

Computing Surveys, Vol. 6, No. 4, December 1974 Structured Programming with go to Statements • 269 output for this example, I found that the Aim+ 1] was already invalid in the previous improvement in performance was not quite line. Similarly, in Example 1 there van be no as much as I had expected. range error in the for loop unless a range error occurred earlier. It seems senseless to Error Exits have expensive range cheeks in those parts For simplicity I have avoided a very impor- of my programs that I know are clean. tant issue in the previous examples, but it In this respect I should mention I-Ioare's must now be faced. All of the programs we almost persuasive arguments to the contrary have considered exhibit bad programming [40, p. 18]. He points out quite correctly that. practice, since they fail to make the neces- the current practice of compiling subscript sary check that m has not gone out of range. range checks into the machine code while a In each case before we perform "m := i" we program is being tested, then suppressing the should precede that operation by a test such checks during production runs, is like a sailor as who wears his life preserver while training on land but leaves it behind when he sails[ if m = max then go to memory overflow; On the other hand, that sailor isn't so foolish where max is an appropriate threshold value. if life vests are extremely expensive, and if he I left this statement out of the examples is such an excellent swimmer that the chance since it would have been distracting, but we of needing one is quite small compared with need to look at it now since it is another the other risks he is taking. In the foregoing important class of go to statements: an examples we typically are much more cer- er~vr exit. Such checks on the validity of tain that the subscripts will be in range than data are very important, especially in soft- that other aspects of our overall program will ware, and it seems to be the one class of go work correctly. John Coeke observes that to's that still is considered ugly but neces- time-consuming range checks can be avoided sary by today's leading reformers. (I wonder by a smart compiler which first compiles the how Val Schorre has managed to avoid such checks into the program then moves them go to's during all these years.) out of the loop. Wirth [94] and ttoare Sometimes it is necessary to exit from [39] have pointed out that a well-designed several levels of control, cutting across code for statement can permit even a rather that may even have been written by other simple-minded compiler to avoid most range programmers; and the most graceful way to checks within loops. do this is a direct approach with a go to or I believe that range checking should be its equivalent. Then the intermediate levels used far more often than it currently is, but of the program can be written under the not everywhere. On the other hand I am assumption that nothing will go wrong. really assuming infallible hardware when I I will return to the subject of error exits say this; surely I wouldn't want to remove later. the parity check mechanism from the hard- ware, even under a hypothetical assumption Subscript Checking that it was slowing down the computation. In the particular examples given above we Additional memory protection is necessary can, of course, avoid testing m vs. max if to prevent my program from harming some- we have dynamic range-checking on all sub- one else's, and theirs from clobbering mine. scripts of A. But this usually aborts the My arguments are directed towards com- program, giving us little or no control over piled-in tests, not towards the hardware the error recovery; so we probably want to mechanisms which are reallj~ needed to en- test m anyway. And ouch, what subscript sure reliability. checking does to the inner loop execution times! In Example 2, I will certainly want to Hash Coding suppress range-checking in the while clause Now let's move on to another example, based since its subscript can't be out of range unless on a standard hashing technique but other-

Computing Surveys, Vol. 6, No. 4, December 1974 ! 270 • Donald E. Knuth

I wise designed for the same application as the since this formulation abstracts the real above. Here h(x) is a hash function which meaning of what!is happening. Someday takes on values between 1 and m; and x ~ 0. there may be hardware capable of testing In this case m is somewhat larger than the membership in small sets more efficiently number of items in the table, and "empty" than if we program the tests sequentially, positions are represented by 0. so that such a program would lead ~o better code than Example 3. And there is a much Example 3: more important reason for preferring this i := h(x); form of the while clause: it reflects a sym- while A[i] # 0 do metry between 0 and x that is not present in begin if A[i] = x then go to found fi; Example 3. For example, in most software i := i--1; ifi = 0 then i := m fi; end; applications it turns out that the condition not found: A[i] := x; B[i] := 0; A[~] -- x terminates the loop far more fie- found: B[i] := B[i]+I; quently than A[~] = 0; with this knowledge, If we analyze this as we did Example 1, my second draft of the program would be the following. we see that the trick which led to Example 2 doesn't work any more. Yet if we want to Example 3a: eliminate the go to we can apply the idea of Example la by writing i :ffi h(x); while A[i] ~ x do while A[i] ~ 0 and h[i] ~ x do . . . begin if A[i] = 0 then A[i] := x; B[i] :-- 0; and by testing afterwards which condition go to found; caused termination. This version is perhaps fi; a little bit easier to read; unfortunately it i := i-1;ifi = 0theni := raft; makes a redundant test, which we would like end; found: B[i] :ffi B[il+I; to avoid if we were in a critical part of the program. This program is easy to derive from the Why should I worry about the redundant go to-less form, but not from Example 3; test in this case? After all, the extra test and it is better than Example 3. So, again we whether A[i] was ~ 0 or ~ x is being made see the advantage of delaying optimizations outside of the while loop, and I said before until we have obtained more knowledge of a that we should generally ecnfine our optimi- program's behavior. zations to inner loops. Here, the reason is It is instructive to consider Example 3a that this while loop won't usually be a loop further, assuming now that the while loop at all; with a proper choice of h and m, the is performed many times per search. Al- operation i := i-1 will tend to be executed though this should not happen in most ap- very infrequently, often less than once per plications of hashing, there are other pro- search on the average [54, Section 6.4]. Thus, grams in which a loop of the above form is the entire program of Example 3, except per- present, so it is worth examining what we haps for the line labeled "not found", must should do in such circumstances. If the while be considered as part of the inner loop, if loop becomes an inner loop affecting the this search process is a dominant part of the overall program speed, the whole picture overall program (as it often is). The redund- changes; that redundant test outside the loop ant test will therefore be significant in this becomes utterly negligible, but the test case. "if i = 0" suddenly looms large. We gen- Despite this concern with efficiency, I erally want to avoid testing conditions that should actually have written the first draft are almost always false, inside a critical of Example 3 without that go to statement, loop. Therefore, under these new assump- probably even using a while clause written tions I would change the data structure by in an extended language, such as adding a'new element A[0] = 0 to the array whileA[i] ~ {0, x}do... and eliminating the test for i ffi 0 as follows.

Computing Surveys, Vol. 6, No. 4, December 1974 Structured Programming with go to E~nts • '271

Example 3b: "carriage return" (i.e., ito advance in the i := h(~); output to the beginning of the next line). •while A[i] ~ x do After printing a period (".") we also want to if A[i] ~ 0 insert an additional spac e in the output. The then i := i-1 following code clearly does the trick. else if i = 0 then i := m; Example 4: else A[i] := x; B[i] := 0; go to found; x :ffi read char; fi; if ~ = alash fi; then x := read char; found: B[il := B[i]+I; if x = slash then return the carriage; The loop now is noticeably faster. Again, I go to char processed; would be unhappy with slow subscript range else tabulate; fi; checks if this loop were critical. Incidentally, fi; Example 3b was derived from Example 3a, write char (x); and a rather different program would have if x = period then write char (space) fi; emerged if the same idea had been applied char processed: to Example 3; then a test "if i = 0" would An abstract program with similar charac- have been inserted outside the loop, at label teristics has been studied by Peterson et al. "not found", and another go to would have [77; Fig. l(a)]. In practice we occasionally been introduced by the optimization process. run into situations where a sequence of As in the first examples, the program in decisions is made via nested if-then-else's, Example 3 is flawed in failing to test for and then two or more of the branches merge memory overflow. I should have done this, into one. We can manage such decision-table for example by keeping a count, n, of how tasks without go to's by copying the com- many items are nonzero. The "not found" mon code into each place, or by defining it routine should then begin with something as a procedure, but this does not seem con- like "n := n-kl; if n = m then go to ceptually simpler than to make go to a com- memory overflow". mon part of the program in such cases. Thus Text Scanning in Example 4 I could avoid the go to by The first time I consciously applied the top- copying "write char (x); ff x ~ pcr/od then down structured programming methodology write char (space) fi" into the program after to a reasonably complex job was in the late "tabulate;" and by making corresponding summer of 1972, when I wrote a program to changes. But this would be a pointless waste prepare the index to my book Sorting and of energy just to eliminate a perfectly under- Searching [54]. I was quite pleased with the standable go to statement: the resulting way that program turned out (there was program would actually be harder to main- tain than the former, since the action of only one serious bug), but I did use one go to statement. In this case the reason was some- printing a character now appears in two what different, having nothing to do with different places. The alternative of declaring exiting from loops; I was exiting, in fact, procedures avoids the latter problem, but it from an if-then-else construction. is not especially attractive either. Still The following example is a simplified ver- another alternative is: sion of the situation I encountered. Suppose Example 4a: we are processing a of text, and that x :-- re~td char; we want to read and print the next character double slash := false; from the input; however, if that character is if x = slash a slash ("/") we want to "tabulate" instead then x := read char; (i.e., to advance in the output to the next if x = slash then double slash :ffi true; tab-stop position on the current line); how- else tabulate; ever, two consecutive slashes means a fi;

Computing Surveys, Vot~ 6, No..4, Deaember-.l~4 [ -,

- ~ ,~,.~4d~.~:,-"i~z:~.:~.¢~! "¸¸ • 272 • DonaldE. Knuth

fi; statements. In addition, I also know of if double slash places where I l~ave myself used a compli- then return the carriage; else write char(x); cated structure with excessively unrestrained if x = period then write char (space) fi; go to statements, especially the notorious fi; Algorithm 2.3.3A for multivariate poly- I claim that this is conceptually no simpler nomial addition [50]. The original program than Example 4; indeed, one can argue that had at least three bugs; exercise 2.3.3-14, it is actually more difficult, because it makes "Give a formal proof (or disproof) of the the entire routine aware of the "double slash" validity of Algorithm A", was therefore exception to the rules, instead of dealing with unexpectedly easy. Now in the second edi- it in one exceptional place. tion, I believe that the revised algorithm is correct, but I still don't know any good way A Confession to prove it; I've had to raise the difficulty Before we go on to another example, I must rating of exercise 2.3.3-14, and I hope some- admit what many readers already suspect, day to see the algorithm cleaned up without namely, that I'm subject to substantial bias loss of its efficiency. because I actually have a vested interest in My books emphasize efficiency because go to statements! The style for the series they deal with that are used re- of books I'm writing was set in the early peatedly as building blocks in a large variety 1960s, and it would be too difficult for me to of applications. It is important to keep change it now; I present algorithms in my efficiency in its place, as mentioned above, books using informal English language but when efficiency counts we should also descriptions, and go to or its equivalent is know how to achieve it. almost the only control structure I have. In order to make it possible to derive Well, I rationalize this apparent anachro- quantitative assessments of efficiency, my nism by arguing that: a) an informaI English books show how to analyze machine lan- description seems advantageous because guage programs; and these programs are many readers tell me they automatically expressed in MIXAL, a symbolic assembly read English, but skip over formal code; b) language that explicitly corresponds one- when go to statements are used judiciously for-one to machine language. This has its together with comments stating nonobvious uses, but there is a danger of placing too loop invariants, they are semantically equi- much stress on assembly code. Programs in valent to while statements, except that MIXAL are like programs in machine lan- indentation is missing to indicate the struc- guage, devoid of structure; or, more pre- ture; c) the algorithms are nearly always cisely, it is difficult for our eyes to perceive short, so that accompanying are the program structure. Accompanying com- able to illustrate the structure; d) I try to ments explain the program and relate it to present algorithms in a form that is most the global structure illustrated in flowcharts, efficient for implementation, and high-level but it is not so easy to understand what is structures often don't do this; e) many going on; and it is easy to make mistakes, readers will get pleasure from converting my partly because we rely so much on comments semiformal algorithms into beautifully struc- which might possibly be inaccurate descrip- tured programs in a formal programming tions of what the program really does. It is language; and f) we are still learning much clearly better to write programs in a lan- about control structures, and I can't afford guage that reveals the control structure, to wait for the final consensus. even if we are intimately conscious of the In spite of these rationalizations, I'm hardware at each step; and therefore I will uncomfortable about the situation, because be discussing a structured assembly language I find others occasionally publishing ex- called PL/MIX in the fifth volume of The amples of algorithms in "my" style but art of . Such a language without the important parenthesized com- (analogous to Wirth's PL360 [95]) should ments and/or with unrestrained use of go to really be supported by each manufacturer

Computing Surveys, Vol. 6, No. 4, December 1974 Structured Programming with go to Statenwnt,,~ • 273 for each machine in place of the old-fash- weeks ago I decided to choose an algorithm ioned structureless assemblers that still pro- at random from my books, to study its use liferate. of go to statements. The very first example On the other hand I'm not really un- I encountered [54, Algorithm 6.2.3C] turned happy that MIxAL programs appear in my out to be another case where existing pro- books, because I believe that MIXAL is a gramming languages have no good substitute good example of a "quick and dirty assem- for go to's. In simplified form, the loop bler", a genre of software which will always where the trouble arises can be written as be useful in its proper role. Such an assembler follows. is characterized by language restrictions Example 5: that make simple one-pass assembly possible, and it has several noteworthy advantages compare: when we are first preparing programs for a if A[i] < x new machine: a) it is a great improvement then if L[i] # 0 over numeric machine code; b) its rules are then i := L[i]; go to compare; else L[i] := j; go to insert fi; easy to state; and c) it can be implemented else if R[i] # 0 in an afternoon or so, thus getting an effi- then i := R[i]; go to compare; cient assembler working quickly on what else R[i] :-- j; go to insert fi; may be very primitive equipment. So far I fi; insert: A[j] := x; have implemented six such assemblers, at L[j] := 0; R[j] := 01j := j+l; different times in my life, for machines or interpretive systems or microprocessors that This is part of the well-known "tree search had no existing software of comparable and insertion" scheme, where a binary search utility; and in each case other constraints tree is being represented by three arrays: made it impractical for me to take the extra A[i] denotes the information stored at node time necessary to develop a good, structured number i, and L[i], R[~] are the respective assembler. Thus I am sure that the concept node numbers for the roots of that node's of quick-and-dirty-assembler is useful, and left and right subtrees; empty subtrees are I'm glad to let MIXAL illustrate what one is represented by zero. The program searches like. However, I also believe strongly that down the tree until finding an empty sub- such languages should never be improved to tree where x can be inserted; and variable j the point where they are too easy or too points to an appropriate place to do the pleasant to use; one must restrict their use insertion. For convenience, I have assumed to primitive facilities that are easy to imple- in this example that x is not already present ment efficiently. I would never switch to a in the search tree. two-pass process, or add complex pseudo- Example 5 has four go to statements, but operations, -facilities, or even fancy the control structure is saved from obscurity error diagnostics to such a language, nor because the program is so beautifully sym- would I maintain or distribute such a metric between L and R. I h-low that these language as a standard for go to statements can be eliminated by a real machine. All such ameliorations and introducing a Boolean variable which be- refinements should appear in a structured comes true when L[i] or R[i] is found to be assembler. Now that the technology is zero. But I don't want to test this variable available, we can condone unstructured in the inner loop of my program. languages only as a bootstrap-like means to a limited end, when there are strong eco- Systematic Elimination nomic reasons for not implementing a better A good deal of theoretical work has been system. addressed to the question of go to elimina- tion, and I shall now try to summarize the Tree Searching findings and to discuss their relevance. But, I'm digressing from my subject of go to S. C. Kleene proved a famous theorem in elimination in higher level languages. A few 1956 [48] which says, in essence, that the set

Computing Surveys, Vol. 6, No. 4, December 1974 274 • Donald E. Knuth

of all paths through any can be p :-- 1; represented as a "regular expression" R while p > 0 do begin if p = 1 then perform step 1; built up from the following operations: p := successor of step 1 fi; if p = 2 then perform step 2; 8 the single arc s of the flowchart p := successor step 2 fi; R1; R2 concatenation (all paths consisting ... of a path of R~ followed by a path if p = nn then perform step n; of R~) p := successor of step n fi; R1 O R2 union (all paths of either R~ or R2) end. R+ iteration (all paths of the form p~; p2; "" ; p~ for some n )_ 1, Here the auxiliary variable p serves as a where each p~ is a path of R) program counter representing which box of the flowchart we're in, and the program stops These regular expressions correspond when p is set to zero. We have eliminated all loosely to programs consisting of statements go to's, but we've actually lost all the struc- in a programming language related by the ture. three operations of sequential composition, Jacopini conjectured in his paper that conditionals (if-then-else), and iterations auxiliary variables are necessary in general, (while loops). Thus, we might expect that and that the go to's in a program of the these three program control structures would form be sufficient for all programs. However, closer analysis shows that Kleene's theorem Ll: if Bi then go to L2 fi; $1; does not relate directly to control structures; if B~ then go to L~ fi; the problem is only superficially similar. His S~; result is suggestive but not really applicable go to L1; in this case. L~: S,; The analogous result for control struc- cannot always be removed unless additional tures was first proved by G. Jacopini in 1966, computation is done. Floyd and I proved this in a paper written jointly with C. BShm conjecture with 's help [52]. [8]. Jacopini showed, in effect, that any Sharper results were later obtained by Ash- program given, say, in flowchart form can be croft and Manna [1], Bruno and Steiglitz transformed systematically into another [10], Kosaraju [57], and Peterson, Kasami, program, which computes the same results and Tokura [77]. and which is built up from statements in the Jaeopini's original construction was not original program using only the three basic merely the trivial flowchart emulation operations of composition, conditional, and scheme indicated above; he was able to iteration, plus possible assignment state- salvage much of the given flowchart struc- meats and tests on auxiliary variables. Thus, ture if it was reasonably well-behaved. A in principle, go to statements can always be more general technique of go to elimination, removed. A detailed exposition of Jacopini's devised by Ashcroft and Manna [1], made construction has been given by H. D. Mills it possible to capture still more of a given [69]. program's natural flow; for example, their Recent interest in structured programming technique applied to Example 5 yields has caused many authors to cite Jacopini's result as a significant breakthrough and as a Example 5a: cornerstone of modern programming tech- t := true; nique. Unfortunately, these authors are un- while t do aware of the comments made by Cooper in begin if A[i] < x 1967 [16] and later by Bruno and Steiglitz then if L[i] # 0 then i := L[i]; [10], namely, that from a practical stand- else L[i] := j; t := false fi; point the theorem is meaningless. Indeed, else if R[i] # 0 then i := R[i]; else R[i] := j; t := false fi; any program can obviously be put into the end; "beautifully structured" form AUI := x;

Computing Surveys, Vol. 6, No. 4, December 1974 Structured Programming with go to Statements • 275

But, in general, their technique may cause a living conditions that are much harder to program to grow exponentially in size; and quantify. when error exits or other recalcitrant go Probably the worst mistake any one can to's are present, the resulting programs will make with respect to the subject of go to indeed look rather like the flowchart emula- statements is to assume that "structured- tor sketched above. programming" is achieved by writing pro- If such automatic go to elimination grams as we always have and then elimi- procedures are applied to badly structured nating the go to's. Most go to's shouldn't programs, we can expect the resulting pro- be there in the first place! What we really grams to be at least as badly structured. want is to conceive of our program in such Dijkstra pointed this out already in [23], a way that we rarely even think about go to saying: statements, because the real need for them hardly ever arises. The language in which we The exercise to translate an arbitrary flow more or less mechanically into a express our ideas has a strong influence on jumpless one, however, is not to be recom- our thought processes. Therefore, Dijkstra mended. Then the resulting flow diagram [23] asks for more new language features-- cannot be expected to be more transparent structures which encourage clear thinking-- than the original one. in order to avoid the go to's temptations to- In other words, we shouldn't merely ward complications. remove go to statements because it's the fashionable thing to do; the presence or Event Indicotors absence of go to statements is not really the The best such language feature I know has issue. The underlying structure of the recently been proposed by C. T. Zahn program is what counts, and we want only [102]. Since this is still in the experimental to avoid usages which somehow clutter up stage, I will take the liberty of modifying the program. Good structure can be expressed his "syntactic sugar" slightly, without in FORTRAN or COBOL, or even in assembly changing his basic idea. The essential novelty language, although less clearly and with in his approach is to introduce a new quan- much more trouble. The real goal is to tity into programming languages, called an formulate our programs in such a way that event indicator (not to be confused with they are easily understood. concepts from PL/I or SIMSC~IPT). My Program structure refers to the way in current preference is to write his event- which a complex algorithm is built up from driven construct in the following two general successively simpler processes. In most forms. situations this structure can be described A) loop until (eventh or -.- or {event)s: very nicely in terms of sequential composi- (statement list)0; tion, conditionals, simple iterations, and repeat; with case statements for multiway branches; then (event)l = > (statement list)l; undisciplined go to statements make pro- (event)~ = > (statement list)n; gram structure harder to perceive, and they fi; are often symptoms of a poor conceptual formulation. But there has been far too B) begin until (event)l or ... or (event)n; much emphasis on go to elimination instead (statement list)0; of on the really important issues; people end; have a natural tendency to set up all easily then (event)t = > (statement list)t; understood quantitative goal like the aboli- ievent)~ = > (statement list)z; tion of jumps, instead of working directly fi: for a qualitative goal like good program structure. In a similar way, many people There is also a new statement, "(event)", have set up "zero population growth" as a which means that the designated event has goal to be achieved, when they really desire occurred: such a statement is allowed only

Computing Surveys, VoL 6, No. 4, December 1974

i 276 • Donald E. Knuth

within (statement lisQ0 of an until con- This use of events is, in fact, semantically struct which declares that event. equivalent to a restricted form of go to In form (A), (statement list)0 is executed statement, which Peter Landin discussed repeatedly until control leaves the construct in 1965 [58] before most of us were ready to • entirely or until one of the named events listen. Landin's device has been reformulated occurs; in the latter case, the statement by Clint and Hoare [14] in the following list corresponding to that event is executed. way: Labels are declared at the beginning The behavior in form (B) is similar, except of each , just as procedures normally that no iteration is implied; one of the named are, and each label also has a (label body) events must have occurred before the end just as a procedure has a (procedure body). is reached. The then.., fi part may be Within the block whose heading contains omitted when there is only one event name. such a declaration of label L, the statement The above rules should become clear go to L according to this scheme means after looking at what happens when Example "execute the body of L, then leave the 5 above is recoded in terms of this new fea- block". It is easy to see that this is exactly ture: the form of control provided by Zahn's event mechanism, with the (label body)s Example 5b: replaced by (statement list)s in the then • • • loop until left leaf hit or fi postlude and with (event) statements right leaf hit: corresponding to Landin's go to. Thus, if A[i] < x Clint and Hoare would have written Ex- then if L[i] # 0 then i := L[i]; else left leaf hit fi; ample 5b as follows. else if R[i] # 0 then i := R[i]; else right leaf hit fi; while true do fi; begin label left leaf hit; L[i] := j; repeat; label right leaf hit; R[i] := j; then left leaf hit = > L[i] := j; if A[i] < x right leaf hit = > R[i] := j; then if L[i] # 0then i := L[i]; fi; else go to left leaf hit fi; A[j] := x; L[j] := 0; R[j] := 0; j := j+l; else if R[/] # 0 then i := R[/]; else go to right leaf hit fi; Alternatively, using a singleevent, end; A[j] := x; L[j] := 0; R[j] := 0; j := j+l; Example 5c: I believe the program reads much better in loop until leaf replaced: if A[i] < x Zahn's form, with the (label body)s set in then if L[i] # 0 then i := L[i] the code between that which logically else L[i] := j; leaf replaced fi; precedes and follows. else if R[i] # 0 then i := R[i] Landin also allowed his "labels" to have else R[i] := j; leaf replaced fi; parameters like any other procedures; this fi; repeat; is a valuable extension to Zahn's proposal, A[j] := x; L[j] :~ O; R[j] := O; j := j+l; so I shall use events with value parameters in several of the examples below. For reasons to be discussed later, Example As Zahn [102] has shown, event-driven 5b is preferable to 5c. statements blend well with the ideas of It is important to emphasize that the first structured programming by stepwise refine- line of the construct merely declares the ment. Thus, Examples 1 to 3 can all be cast event indicator names, and that event into the following more abstract form, using indicators are not conditions which are being an event "found" with an integer parameter: tested continually; (event) statements are simply transfers of control which the com- begin until found: piler can treat very efficiently. Thus, in search table for x and Example 5e the statement "leaf replaced" insert it if not present; end; is essentially a go to which jumps out of then found (integer j) => B[j] := B[j]+I; the loop. fi;

Computing Su~eys, VoL 6, No. 4, December 1974 Structured Programming with go to Sta~mcnto • • '277

This much of the program can be written if x = slash before we have decided how to maintain then double slash; else tabu/ate; the table. At the next level of abstraction, normal character input (x); we might decide to represent the table as a fi; sequential list, as in Example 1, so that else normal character input (x); "search table ... " would expand into fi) end; for i := 1 step 1 until m do then normal character input (char x) ffi > if A[i] = x then found(i) fi; write char (x) ; m := m~-l; Aim] := x; found(m); if x --- period then write char (space) fi; double slash = > return the carriage, Note that this for loop is more disciplined fi; than the one in our original Example 1, This program states the desired actions a because the iteration variable is not used bit more clearly than any of our previous outside the loop; it now conforms to the rules attempts were able to do. of ALGOL W and ALGOL 68. Such for loops Event indicators, handle error exits too. provide convenient documentation and For example, we might write a program as avoid common errors associated with global follows. variables; their advantages have been discussed by Hoare [39]. begin until error or normal end: Similarly, if we want to use the idea of if m = max then error ('symbol table full') fi; Example 2 we might write the following code as the refinement of "search table •.."" normal end; begin integer i; end; A[m-bl] := x;i := 1; then error (string E) ffi while A[i] ~ x do i := i-bl; print ('unrecoverable error,'; E); ifi > mthenm := i;B[m] := 0fi; normal end = > found (/) ; print ('computation complete'); end; fi; And finally, if we decide to use hashing, Comparison of Features we obtain the equivalent of Example 33 Of course, event indicators are not the only which might be written as follows. decent alternatives tO go to statements that have been proposed. Many authors begin integer i; i := h(x); have suggested language features which loop until present or absent: provide roughly equivalent facilities, but if A[i] = x then present fi; which are expressed in terms of exit, jump- if A[i] = 0 then absent fi; out, break, or leave statements. Kosaraju i:=i-1; [57] has proved that such statements are ifi = 0theni := mfi; repeat; sufficient to express all programs without then present = > found(i); go to's and without any extra computation, absent => A[i] := x; found(/); but only if an exit from arbitrarily many fi; levels of control is permitted. end; The earliest language features of this kind The begin until (event) construct ulso (besides Landin's proposal) provided essen- provides a natural way to deal with decision- tially only one exit from a loop; this means table constructions such as the text-scanning that the code appearing in the then ... fi application we have discussed. postlude of our examples would be inserted Example 4b: into the body itself before branching. (See Example 5c.) The separation of such code begin until normal character input as in Zahn's proposal is better, mainly or double slash: because the body of the construct corre- char x; x := read char; sponds to code that is written under different if x = slash "invariant assumptions" which are inopera- then x := read char~~ tive after a particular event has occurred.

Computing Surveys, Vol. 6, No. 4, December 1~4 278 • Donald E. Knuth

Thus, each'event corresponds to a particular specified block of code, and the block may set of assertions about the state of the be dynamically respecified. program, and the code which follows that Some people have suggested to me that event takes cognizance of these assertions, events should be called "conditions" instead, which are rather different from the assertions by analogy with Boolean expressions. How- in the main body of the construct. (For this ever, that terminology would tend to imply a reason I prefer Example 5b to Example 5c.) relation which is continually being moni- Language features allowing multiple exits tored, instead of a happening. By writing have been proposed by G. V. Bochmann [7], "loop until yprime is near y: ..." we seem and independently by Shigo et al. [86]. These to be saying that the machine should keep are semantically equivalent to Zahn's pro- track of whether or not y and yprime are posals, with minor variations; but they nearly equal; a better choice of words would express such semantics in terms of state- be an event name like "loop until con- ments that say "exit to (label)". I believe vergence established:.-." so that we can Zahn's idea of event indicators is an im- write "if abs(yprime - y) < epsilon X y provement on the previous schemes, because then convergence established". An event the specification of events instead of labels occurs when the program has discovered encourages a better conception of the pro- that the state of computatioD has changed. gram. The identifier given to a label is often an imperative verb like "insert" or "com- Simple Iterations pare", saying what action is to be done next, So far I haven't mentioned what I believe while the appropriate identifier for an event is really the most common situation in which is more likely to be an adjective like "found". go to statements are needed by an ALGOL The names of .events are very much like the or PL/I programmer, namely a simple names of Boolean variables, and I believe iterative loop with one entrance and one this accounts for the popularity of Boolean exit. The iteration statements most often variables as documentation aids, in spite of proposed as alternatives to go to statements their inefficiency. have been "while B do S" and "repeat S Putting this another way, it is much until B". However, in practice, the itera- better from a psychological standpoint to tions I encounter very often have the form write A: S; loop untll found • • • ; found; • • • repeat if B then go to Z fi; T; go to A; than to write Z: search: while true do where S and T both represent reasonably begin ... ; leave search; .-. end. long sequences of code. If S is empty, we The leave or exit statement is operationally have a while loop, and if T is empty we the same, but intuitively different, since it have a repeat loop, but in the general case talks more about the program than about it is a nuisance to avoid the go to state- the problem. ments. The PL/I language allows programmer- A typical example of such an iteration defined ON-conditions, which are similar occurs when S is the code to acquire or in spirit to event indicators. A programmer generate a new piece of data, B is the test first executes a statement "ON CONDITION for end of data, and T is the processing of (identifier) block" which specifies a block that data. Another example is when the code of code that is to be executed when the preceding the loop sets initial conditions identified event occurs, and an occurrence for some iterative process; then S is a com- of that event is indicated by writing SIG- putation of quantities involved in the test NAL CONDITION (identifier). However, for convergence, B is the test for conver- the analogy is not very close, since control gence, and T is the adjustment of variables returns to the statement following the for the next iteration. SIGNAL statement after execution of the Dijkstra [29] aptly named this a loop

Computing Surveys, Vol. 6, No. 4, December 1974 Structured Programming with go to Statements • 279 which is performed "n and a half times". our language would provide a single feature The usual practice for avoiding go to's in which covered all simple iterations without such loops is either to duplicate the code going to a rather "big" construct like the for S, writing event-driven scheme. When a programmer uses the simpler feature he is thereby making S; while B do begin T; S end; it clear that he has a simple iteration, with where B is the negation of relation B; or to exactly one condition which is being tested figure out some sort of "inverse" for T so exactly once each time around the loop. that "T-i; T" is equivalent to a null state- Furthermore, by providing special syntax ment, and writing for this common case we make it easier for a compiler to produce more efficient code, T-l; repeat T; S until B; since the compiler can rearrange the machine or to duplicate the code for B and to make a instructions so that the test appears physi- redundant test, writing cally at the end of loop. (Many hours of computer time are now wasted each day repeat S; if B then T fi; until B; executing unconditional jumps to the be- or its equivalent. The reader who studies ginning of loops.) go to-less programs as they appear in the Ole-Johan Dahl has recently proposed a literature will find that all three of these syntax which I think is the first real solution rather unsatisfactory constructions are used to the n -{- ~ problem, He suggests writing frequently. the general simple iteration defined above as I discussed this weakness of ALGOL in a loop; S; while B: T; repeat; letter to in 1967, and he proposed two solutions to the problem, where, as before, S and T denote sequences together with many other instructive ideas of one or more statements separated by in an unpublished report on basic concepts semicolons. Note that as in two of our of programming languages [94]. His first original go to-free examples, the syntax suggestion was to write refers to condition B which represents repeat begin S; when B exit; T; end; staying in the iteration, instead of condition B which represents exiting; and this may and readers who remember 1967 will also be the secret of its success. appreciate his second suggestion, Dahl's syntax may not seem appropriate turn on begin S; when B drop out; T; end. at first, but actually it reads well in every example I have tried, and I hope the reader Neither set of delimiters was felt to be will reserve judgment until seeing the ex- quite right, but a modification of the first amples in the rest of this paper. One of the proposal (allowing one or more single-level nice properties of his syntax is that the word exit statements within repeat begin... repeat occurs naturally at the end of a loop end) was later incorporated into an experi- rather than at its beginning, since we read mental version of the ALGOL W language. the actions of the program sequentially. Other languages such as BCPL and BLISS As we reach the end, we are instructed to incorporated and extended the exit idea, as repeat the loop, instead of being informed mentioned above. Zahn's construction now that the text of the loop (not its execution) allows us to write, for example, has ended. Furthermore, the above syntax loop until all data exhausted: avoids ALGOL'S use of the word do (and S; also the more recent unnatural delimiter if B then all data exhausted fi; od); the word do as used in ALGOL has T; never sounded quite right to native speakers repeat; of English, it has always been rather quaint and this is a better syntax for the n + for us to say "do read (A[i])" or "do begln"! problem than we have had previously. Another feature of Dahl's proposals is that On the other hand, it would be nicest if it is easily axiomatized along the lines

Computing Surveys, Vol. 6, No. 4, December 1974 i 280 • Donald E. Knuth proposed by Hoare [37, 41]: 2. INTRODUCTION OF go to STATEMENTS I {P}SiQ} Now that I have discussed how to remove {Q A B}T{P} go to statements, !I will turn around and {P} loop: S; while B: T; repeat; {Q A ~ B} show why there are occasions when I actually wish to insert them into a go to-less program. (Here I am using braces around the asser- The reason is that I like well-documented tions, as in Wirth's PASCAL language [97], programs very much, but I dislike inefficient instead of following Hoare's original nota- ones; and there are some cases where I tion "P {S} Q", since assertions are, by simply seem to need go to statements, nature, parenthetical remarks.) despite the examples stated above. The nicest thing about Dahl's proposal is that it works also when S or T is empty, Recursion Elimination so that we have a uniform syntax for all Such cases come to light primarily when I'm three cases; the while and repeat state- trying to optimize a program (originally ments found in ALGoL-like languages of the well-structured), often involving the removal late 1960s are no longer needed. When S or of implicit or explicit recursion. For example, T is empty, it is appropriate to delete the consider the following recursive procedure preceding colon. Thus that prints the contents of a binary tree in loop while B: symmetric order. The tree is represented by T; L, A, and R arrays as in Example 5, and the repeat; recursive procedure is essentially the defini- tion of symmetric order. takes the place of "while B do begin T end;" and Example 6: loop: procedure treeprint(O; integer t; value t; S if t#0 while B repeat; then treeprint(L[t]) ; print (A[tl) ; takes the place of "repeat S until B;". At treeprint (R[t]); fi; first glance these may seem strange, but probably less strange than the while and This procedure may be regarded as a repeat statements did when we first learned model for a great many algorithms which them. have the same structure, since tree traversal If I were designing a programming lan- occurs in so many applications; we shall guage today, my current preference would assume for now that printing is our goal, be to use Dahl's mechanism for simple with the understanding that this is only one iteration, plus Zahn's more general con- instance of a generM family of algorithms. struct, plus a for statement whose syntax It is often useful to remove recursion would be perhaps from an algorithm, because of important economies of space or time, even though this loop forl < i < n: tends to cause some loss of the program's S; repeat; basic clarity. (And, of course, we might also have to state our algorithm in a language with appropriate extensions. These control like FORTRAN or in a machine language that structures, together with if... then-.. doesn't allow recursion.) Even when we use else .-. fi, will comfortably handle all the ALGOL or PL/I, every compiler I know im- examples discussed so far in this paper, poses considerable overhead on procedure without any go to statements or loss of calls; this is to a certain extent inevitable efficiency or clarity. Furthermore, none of because of the generMity of the parameter these language features seems to encourage mechanisms, especially cM1 by name and the overly-complicated program structure. maintenance of proper dynamic environ-

Computing Surveys, Vol. 6, No. 4, December 1974 Structured Programming with go to 8~atements • 281 ments. When procedure calls occur in an above simplification makes q resume the inner loop the overhead can slow a program caller of p. When q ffi p the argument is down by a factor of two or more. But if we perhaps a bit subtle, but it's all right. (I'm hand tailor our own implementation of not sure who originated this principle; I recursion instead of relying on a general recall learning it from Gill's paper [34, p. mechanism we can usually find worthwhile 183], and then seeing many instances of it in simplifications, and in the process we occa- connection with top-do~vn compiler organiza- sionally get a deeper insight into the original tion. Under certain conditions the BLms/ll algorithm. compiler [101] is capable of discovering this There has been a good deal published simplification. Incidentally, the converse of about recursion elimination (especially in the the above principle is also true (see [52]): work of Barron [4], Cooper [15], Manna and go to statements can always be eliminated Waldinger [61], McCarthy [62], and Strong by declaring suitable procedures, each of [88; 91]); but I'm amazed that very little of which calls another as its last action. This this is about "down to earth" problems. I shows that procedure calls include go to have always felt that the transformation statements as a special case; it cannot be from recursion to iteration is one of the most argued that procedures are conceptually fundamental concepts of computer science, simpler than go to's, although some people and that a student should learn it at about have made such a claim.) the time he is studying data structures. This As a result of applying the above simplifi- topic is the subject of Chapter 8 in my multi- cation, and adapting it in the obvious way volume work; but it's only by accident that to the case of a procedure with one parame- recursion wasn't Chapter 3, since it concep- ter, Example 6 becomes tually belongs very early in the table of Example 6a: contents. The material just wouldn't fit com- fortably into any of the earlier volumes; yet procedure treeprint(t); integer t; value ~; there are many algorithms in Chapters 1-7 L:ift~0 that are recursions in disguise. Therefore it then treeprint(L[t]) ; print(A[t]) ; surprises me that the literature on recursion t := R[t]; go to L; removal is primarily concerned with "baby" fi; examples like computing factorials or re- versing lists, instead of with a sturdy toddler But we don't really want that go to, so we like Example 6. might prefer to write the code as follows, Now let's go to work on the above ex- using Dahl's syntax for iterations as ex- ample. I assume, of course, that the reader plained above. knows the standard way of implementing Example 6b: recursion with a stack [20], but I want to make simplifications beyond this. Rule procedure treeprint(t); integer t; value t; loop while t ~ 0: number one for simplifying procedure calls treeprint (L[t]) ; is: print (A [t]) ; If the last action of procedure p before it re- t := R[t]; turns is to call procedure q, simply go to the repeat; beginning of procedure q instead. If our goal is to impress somebody, we (We must forget for the time being that we might tell them that we thought of Example don't like go to statements.) It is easy to 6b first, instead of revealing that we got it confirm the validity of this rule, if, for sim- by straightforward simplification of the plicity, we assume parameterless procedures. obvious program in Example 6. For the operation of calling q is to put a re- There is still a recursive call in Example turn address on the stack, then to execute q, 6b; and this time it's embedded in the pro- then to resume p at the return address cedure, so it looks as though we have to go specified, then to resume the caller of p. The to the general stack implementation. How-

Computing Surveys, Vo|. 6, No. 4, De~ember 1~4 282 • Donald E. Knuth ever, the recursive call now occurs in only equally simple way to remove the recursion one place, so we need not put a return without resorting to something like Example address on the stack; only the local variable 6c. As I say, it was a shock when I first ran t needs to be saved on each call. (This is across such an example. Later, Jim Horning another simplification which occurs fre- confessed to me that he also was guilty, in quently.) The program now takes the fol- the syntax-table-building program for the lowing nonrecursive form. XPL system [65, p. 500], because XPL Example 6c : doesn't allow recursion; see also [56]. Clearly a now doctrine about sinful go to's is needed , procedure treeprint(t); integer t; value t; some sort of "situation ethics". begin integer stack S; S := empty; The new morality that I propose may LI: loop while t ~ 0: perhaps be stated thus: "Certain go to <= t; t := L[t]; go to L1; L2: t <= S; statements which arise in connection with print (A[t]) ; well-understood transformations are accept- t := R[t]; able, provided that the program documenta- repeat; tion explains what the transformation was." if nonempty(S) then go to L2 fi; end. The use of four-letter words like goto can occasionally be justified even in the best of Here for simplicity I have extended ALGOL company. to allow a "stack" , where S < = t This situation is very similar to what means "push t onto S" and t < = S means people have commonly encountered when "pop the top of S to t, assuming that S is proving a program correct. To demonstrate nonempty". the validity of a typical program Q, it is It is easy to see that Example 6c is equiva- usually simplest and best to prove that some lent to Example 6b. The statement "go to rather simple but less efficient program P is LI" initiates the procedure, and control correct and then to prove that P can be returns to the following statement (labeled transformed into Q by a sequence of valid L2) when the procedure is finished. Although optimizations. I'm saying that a similar Example 6c involves go to statements, their thing should be considered standard prac- purpose is easy to understand, given the tice for all but the simplest software pro- knowledge that we have produced Example grams: A programmer should create a pro- 6c by a mechanical, completely reliable gram P which is readily understood and method for removing recursion. Hopkins well-documented, and then he should op- [44] has given other examples where go to timize it into a program Q which is very effi- at a low level supports high-level construc- cient. Program Q may contain go to state- tions. ments and other low-level features, but the But if you look at the above program transformation from P to Q should be ac- again, you'll probably be just as shocked as complished by completely reliable and well- I was when I first realized what has hap- documented "mechanical" operations. pened. I had always thought that the use of At this point many readers will say, "But go to statements was a bit sinful, say a he should only write P, and an optimizing "venial sin"; but there was one kind of go to compiler will produce Q." To this I say, that I certainly had been taught to regard "No, the optimizing compiler would have to as a mortal sin, perhaps even unforgivable, be so complicated (much more so than any- namely one which goes into the middle of an thing we have now) that it will in fact be iteration! Example 6c does precisely that, unreliable." I have another alternative to and it is perfectly easy to understand Exam- propose, a new class of software which will ple 6c by comparing it with Example 6b. be far better. In this particular case we can remove the go to's without difficulty; but in general Program ManipulationSystems when a recursive call is embedded in For 15 years or so I have been trying to ~r "1 ""~'~ a rc~,~,,~ till .~ ~,,,~,~u~u ,., think of how to write a compiler that really several complex levels of control, there is no produces top quality code. For example,

Computing Surveys, Vol; 6, No. 4, December 1974 Structured Programming with go to Statements • 283 most of the Mix programs in my books are restructuring of a program by combining considerably more efficient than any of similar loops. I'later discovered that program today's most visionary compiling schemes manipulation is just part of a much more would be able to produce. I've tried to study ambitious project undertaken by Cheatham the various techniques that a hand-coder and Wegbreit [12]; another paper about like myself uses, and to fit them into some source-code optimizations has also recently systematic and automatic system. A few appeared [83]. Since LIsP programs are easily years ago, several students and I looked at a manipulated as LisP d£ta objects, there has typical sample of FORTRAN programs [51], also been a rather extensive development of and we all tried hard to see how a machine similar ideas in this domain, notably by could produce code that would compete Warren Teitelman (see [89, 90]). The time with our best hand-optimized object pro- is clearly ripe for program-manipulation grams. We found ourselves always running systems, and a great deal of further work up against the same problem: the compiler suggests itself. needs to be in a dialog with the prograrmner; The programmer using such a system will it needs to know properties of the data, and write his beautifully-structured, but possibly whether certain cases can arise, etc. And we inefficient, program P; then he will inter- couldn't think of a good language in which actively specify transformations that make to have such a dialog. it efficient. Such a system will be much more For some reason we all (especially me) had powerful and reliable than a "completely a mental block about optimization, namely automatic one. We can also imagine the sys- that we always regarded it ~ a behind-the- tem manipulating measurement statistics scenes activity, to be done in the machine concerning how much of the total running language, which the programmer isn't sup- time is spent in each statement, since the posed to know. This veil was first lifted from programmer will want to know which parts my eyes in the Fall of 1973. when I ran across of his program deserve to be optimized, and a remark by Hoare [42] that, ideally, a how much effect an optimization will really language should be designed so that an have. The original program P should be re- optimizing compiler can describe its optimi- tained along with the transformation specifi- zations in the source language. Of course! cations, so that it can be properly understood Why hadn't I ever thought of it? and maintained as time passes. As I say, this Once we have a suitable language, we will idea certainly isn't my own; it is so exciting be able to have what seems to be emerging I hope that everyone soon becomes aware of as the programming system of the future: an its possibilities. interactive program-manipulation system, A "calculus" of program transformations analogous to the many symbol-manipulation is gradually emerging, a set of operations systems which are presently undergoing ex- which can be applied to programs without tensive development and experimentation. rethinking the specific problem each time. We are gradually learning about program I have already mentioned several of these transformations, which are more complicated transformations: doubling up of loops (Ex- than formula manipulations but really not ample 2a), changing final calls to go to's very different. A program-manipulation sys- (Example 6a), using a stack for recursions tem is obviously what we've been leading up (Example 6c), and combining disjoint loops to, and I wonder why I never thought of it over the same range [18]. The idea of macro- before. Of course, the idea isn't original with expansions in general seems to find "its most me; when I told Hoare, he said, "Exactly!" appropriate realization as part of a program and referred me to u recent paper by Darling- manipulation system. ton and Burstall [18]. Their paper describes Another well-known example is the re- a system which removes some recursions moval of invariant subexpressions from from a LisP-like language (curiously, without loops. We are all familiar with the fact that introducing any go to's), and which also a program which includes such subexpres- does some conversion of data structures sions is more readable than the corresponding (from sets to lists or bit strings) and some program with invariant subexpressions

Computing Surveys, ~ui. 6, Noo4, December 1974 [ 284 • Donald E. Knuth moved out of their loops; yet we consciously Furthermore, there is a rather simple way remove them when the running time of the to understand this program, by providing program is important. suitable "loop invari~nts". At the beginning Still another type of transformation occurs of the first (outer) loop, suppose the stack when we go from high-level "abstract" data contents from top tO bottom are t,, ..., t~ structures to low-level "concrete" ones (see for some n > 0; then the procedure's re- Hoare's chapter in [17] for numerous ex- maining duty is to accomplish the effect of amples). In the case of Example 6c, we can replace the stack by an array and a pointer, treeprint (t) ; print(A[t,]) ; treeprint(R[t,]) ; arriving at Example 6d: print(A[tl]) ; treeprint(R[tl]) ; (*) procedure treeprint(t); integer t; value t; In other words, the purpose of the stack begin integer array S[1: n]; in teger k; k : = 0; is to record postponed obligations to print LI: loop while t ~ 0: the A's and right subtrees of certain nodes. k := k+l; S[k] := t; t := L[t]; go to L1; Once this concept is grasped, the meaning L2: t := S[k]; k := k-l; of the program is clear and we can even see print (AIt]) ; how we might have written it without ever t := R[t]; thinking of a recursive formulation or a repeat; go to statement: The innermost loop ensures ifk~0then go toL2fi; end. t ffi 0, and afterwards the program reduces the stack, maintaining (*) as the condition Here the programmer must specify a safe to be fulfilled, at key points in the outer loop. value for the maximum stack size n, in order A careful programmer might notice a to make the transformation legitimate. Al- source of inefficiency in this program: when ternatively, he may wish to implement the L[t] = 0, we put t on the stack, then take it stack by a linked list. This choice can usually off again. If there are n nodes in a binary be made without difficulty, and it illustrates tree, about half of them, on the average, will another area in which interaction is prefer- have L[t] ffi 0 so we might wish to avoid able to completely automatic transforma- this extra computation. It isn't easy to do tions. that to Example 6e without major surgery on the structure; but it is easy to modify Recursion vs. Iteration Example 6e (or 6d), by simply bracketing Before leaving the treeprint example, I would the souree of inefficiency, including the go like to pursue the question of go to elimina- to, and the label, and all. tion from Example 6c, since this leads to some interesting issues. It is clear that the Example 6f: first go to is just a simple iteration, and a little further study shows that Example 6e procedure treeprinl(t); value t; integer t; is just one simple iteration inside another, begin integer stack S; S := empty; LI: loop while t ~ 0: namely (in Dahl's syntax) L3: if L[t] ~ 0 Example 6e: then S < = t; t := L[t]; go to L1; L2: t < = S; procedure treeprint(t); integer l; value t; fi; begin integer stack S; S := empty; print(A[t]) ; loop: t := R[tl; loop while t ~ 0: repeat; S<=t; if nonempty(S) then go to L2 fi; t := L[t]; end. repeat; while nonemply(S) : Here we notice that a further simplification t<= S; is possible: go to L1 can become go to L3 print(A[t]); t := Rit]; because t is known to he nonzero. repeat; An equivalent go to-free program analo- end. gous to Example 6e is

Computing Surveys, Vol. 6, No. 4, December 1974 i Structured Programming with go to Statement8 • 285

Example 6g: found that the corresponding reeursive ver- sion took about 2.1 unlfis of time per node procedure treeprint(t); value t; integer t; begin integer stack S; S := empty; using our ALGOLW compiler for the 360/67; loop until finished: and the ratio was 1.16 using the SAIL com- if/ ~0 piler for the PDP-10. (Incidentally, the then relative run-times for Example 6f were 0.8 loop while L[t] ~ 0: S<=t; with ALGOL W, and 0.7 with SAIL. When t := L[t]; subscript ranges were dynamically checked, repeat; ALGOL W took 1.8 units of time per node for e|se the nonrecursive version, and 2.8 with the if nonempty (S) recursive version; SAIL'S figures were 1.28 then t < = S; else finished; and 1.34.) fi; fi; Boolean Variable Elimination print(A[t]) ; t :ffi R[t]; Another important program transformation, repeat; somewhat less commonly known, is the re- end. moval of Boolean variables by code duplica- tion. The following example is taken from I deriv'ed this program by thinking of the Dijkstra's treatment [26, pp. 91-93] of loop invariant (*) in Example 6e and acting Hoare's "Quicksort" algorithm. The idea is accordingly, not by trying to eliminate the to rearrange array elements A[m]... A[n] so go to's from Example 6f. So I know this that they are partitioned into two parts: program is well-structured, and I therefore The left part Aim]... A[j--1], for some haven't succeeded in finding an example of appropriate j, will contain all the elements recursion removal where go to's are strictly less than some value, v; the right part necessary. It is interesting, in fact, that our A[j+ 1]... A[n] will contain all the elements transformations originally intended for effi- greater than v; and the element A[j] lying ciency led us to new insights and to programs between these parts will be equal to v. that still possess decent structure. However, Partitioning is done by scanning from the I still feel that Example 6f is easier to under- left until finding an element greater than v, stand than 6g, given that the reader is told then scanning from the right until finding an the recursive program it comes from and the element less than v, then scanning from the transformations that were used. The recur- left again, and so on, moving the offending sire program is trivially correct, and the elements to the opposite side, until the two transformations require only routine verifi- scans come together; a Boolean variable up cation; by contrast, a mental leap is needed is used to distinguish the left scan from the to invent (*). right. Does recursion elimination help? Clearly there won't be much gain in this example if Example 7 : the print routine itself is the bottleneck. But i := m;j := n; let's replace print(A[t]) by v := A[j]; up := true; i := i-t-1; B[i] := A[t]; loop: if up i.e., instead of printing the tree, let's assume then if A[/] ~> v then A[3] :ffi A[i]; up :ffi false fii that we merely want to transfer its contents else if v > A[j] to some other array B. Then we can expect then A[i] :-: A[j]; up :-- true fi; to see an improvement. fi; After making this change, I tried the re- if up then i :ffi i+1 else j :ffi j--1 fi; while i < j repeat; cursive Example 6 vs. the iterative Example A[j] := v; 6d on the two main ALGOL compilers avail- able to me. Normalizing the results so that The manipulation and testing of up is 6d takes 1.0 units of time per node of the rather time-consuming here. We can, in tree, with subscript checking suppressed, I general, eliminate a Boolean variable by

Computing Surveys, Vo~ 6, No. 4, Deeember lff/4 286 • Donald E. Knuth storing its current value in the program grammer would write for the examples, and counter, i.e., by duplicating the program, the other with the object code produced by letting one part of the text represent true a typical compiler that does only local op- and the other part false, with jumps be- timizations. The assembly-language pro- tween the two parts in appropriate places. grammer will keep i, j, v, and up in registers, Example 7 therefore becomes while a typical compiler will not keep vari- Example 7a: ables in registers from one statement to another, except if they happen to be there i := m;j := n; by coincidence. Under these assumptions, v := A[jl; the asymptotic running time for all entire loop: if A[i] > v then A[j] := A[i]; go to upf fi; Quicksort program based on these routines upt:i := i+1; will be while i < j repeat; go to common; assembled compiled loop: if v > A[j] then A[i] := A[j]; go to upt fi; Example 7 202/~N In N 55~6NIn N upf: j := j--l; Example 7a 15~N In N 40N In N while i < j repeat; expressed in memory references to data and common: A[j] := v; instructions. So Example 7a saves more than Note that again we have come up with a 25 % of the sorting time. program which has jumps into the middle of I showed this example to Dijkstra, cau- iterations, yet we can understand it since we tioning him that the go to leading into an know that it came from a previously under- iteration might be a terrible shock. I was stood program, by way of an understandable extremely pleased to receive his reply [31]: transformation. Of course this program is messier than the Your technique of storing the value of up in the order counter is, of course, absolutely safe. first, and we must ask again if the gain in I did not faint! I am in no sense "afraid" of a speed is worth this cost. If we are writing a program constructed that way, but I cannot sort procedure that will be used many times, consider it beautiful: it is really the same we will be interested in the speed. The repetition with the same terminating condi- average running time of Quicksort was tion, that "changes color" as the computation analyzed by Hoare in his 1962 paper on the proceeds. subject [36], and it turns out that the body He went on to say that he looks forward to of the loop in Example 7 is performed about the day when machines are so fast that we 2N In N times while the statement up := won't be under pressure to optimize our false is performed about ~N In N times, if programs; yet we are sorting N elements. All other parts of For the time being I could not agree mare with the overall sorting program (not shown your closing remarks : if the economies matter, here) have a running time of order N or less, apply "disciplined optimalization" to a nice so when N is reasonably large the speed of program, the correctness of which has been the inner loop governs the speed of the entire established beyond reasonable doubt. Your massaging of the program text is then no sorting process. (Incidentally, a recursive longer trickery ad hoe, it is perfectly safe and version of Quicksort will run just about as sound. fast, since the recursion overhead is not part of the inner loop. But in this case the It is hard for me to express the joy that this removal of recursion is of great value for letter gave me; it was like having all my another reason, because it cuts the auxiliary sins forgiven, since I need no longer feel stack space requirement from order N to guilty about my optimized programs. order log N.) Using these facts about inner loop times, Coroutines we can make a quantitative comparison of Several of the people who read the first draft Examples 7 and 7a. As with Example 1, it of this paper observed that Example 7a can seems best to make two comparisons, one perhaps be understood more easily as the with the assembly code that a decent pro- result of eliminating linkage instead

Comuuting Surveys, Vol. 6, No. 4, December 1974 Structured Programming with go to Statements • 287 of Boolean variables. Consider the following coroutines, or by fo~ming an equivalent program: program which expresses the coroutine link- age in terms of go to statements; it appears Example 7b: to be cumbersome (though not impossible) eoroutine move i; to do the job without using either feature. loop: if A[i] > v then A[j] := A[i]; resume move j; Quicksort.. A Digression fi; Dijkstra also sent another instructive ex- i := i+1; ample in his letter [30]. He decided to create while i < j repeat; the program of Example 7 from scratch, as eoroutine move j; if Hoare's algorithm had never been in- loop: if v > A[j] then A[i] := A[j]; vented, starting instead with modern ideas resume move i; of semi-automatic program construction fi; based on the following invariant relation: j := j--l; while i < j repeat; v = A[n] A i := m;j := n; v := A[jl; Vk(m<_kA[kl_< v) A call move i; Vk(j < k < n = > A[k] _> v). A[jl := v; The resulting program is unusual, yet per- When a coroutine is "resumed", let's as- haps cleaner than Example 7: sume that it begins after its own resume i :ffi m;j := n-i;v := A[nh statement; and when a coroutine terminates, loop while i < j; let's assume that the most recent call state- if A[j] ~ v thenj := j-l; ment is thereby completed. (Actual coroutine else A[i] := : A[j]; i :ffi i+1; linkage is slightly more involved, see Chapter fi; 3 of [17], but this description will suffice for repeat; if j ~ m then Alml := : Alnl; j := m fi; our purposes.) Under these conventions, Example 7b is precisely equivalent to Ex- Here ":= :" denotes the interchange (i.e., ample 7a. At the beginning of move i we swap) operation. At the conclusion of this know that A[k] <_ v for all k < i, and that program, the A array will be different than i < j, and that {A[m], ..-, A[j-1],A[j+I], before, but we will have the array parti- • .. ,A[n]} 0 v is a permutation of the orig- tioned as desired for sorting (i.e., A[m]. • • A[j] inal contents of {A[m], ..., A[n]l; a similar are ~ v and A[j+I]...A[n] are ~v). statement holds at the beginning of move j. Unfortunately, however, this "pure" pro- This separation into two coroutines can be gram is less efficient than Example 7, and said to make Example 7b conceptually sim- Dijkstra noted that he didn't like it very pler than Example 7; but on the other hand, much himself. In fact, Quicksort is really the idea of coroutines admittedly takes some quick in practice because there is a method getting used to. that is even better than Example 7a: A good Christopher Strachey once told me about Quicksort routine will have a faster inner an example which first convinced him that loop which avoids most of the "i < j" tests. coroutines provided an important control Dijkstra recently [31] sent me another ap- structure. Consider two binary trees repre- proach to the problem, which leads to a sented as in Examples 5 and 6, with their A much better solutiom First we can abstract array information in increasing order as we the situation by considering any notions traverse the trees in symmetric order of their "small" and "large" so that: a) an element nodes. The problem is to merge these two A A[i] is never both small and large simultane- array sequences into one ordered sequence. ously; b) some elements might be neither This requires traversing both trees more or small nor large; c) we wish to rearrange an less asynchronously, in symmetric order, so array so that all small elements precede all we'll need two versions of Example 6 running large ones; and d) there is at least one ele- cooperatively. A conceptually simple solu- ment which is not small, and at least one tion to this problem can be written with which is not large. Then we can write the

Computing Surveys, Vo|. 0, No. 4, December 1974 t 288 • Donald E. Knuth following program in terms of this abstrac- • i := m--l; j :ffi n; v := A[n]; tion. loop until pointers have met: loop: i := i+1; while A[i] < v repeat; Example 8: if i _) j then pointers have met; fi A[j] := A[i]; i := m;j := n; loop: j := j-l; while A{j] > v repeat; loop: ifi _> j then j := i; pointers have met; fi loop while A[i] is small: A[i] := A[j]; i := i+1; repeat; repeat; loop while A[j] is large: Afj] := v; j := j-l; repeat; while i < j: At the conclusion of this routine, the A[i] := : A[j]; contents of A[m]... A[n] have been per- i := i+l;j := j--l; muted so that Aim]... A[j-1] are < v repeat; and A[j+I] -.. A[n] are _> v and A[j] = v At the beginning of the first (outer) loop and m < j < n. The assembled version will we know that A[k] is not large for m g k < i, make about llN In N references to memory and that A[k] is not small for j < k < n; on the average, so this program saves 28 % also that there exists a k such that i < k _< n of the running time of Example 7a. and A[k] is not small, and a k such that When I first saw Example 8 I was cha- m < k < j and A[k] is not large. The opera- grined to note that it was easier to prove tions in the loop are easily seen to preserve than my program, it was shorter, and (the these "invariant" conditions. Note that the crushing blow) it also seemed about 3% inner loops are now extremely fast, and that faster, because it tested "i < j" only half they are guaranteed to terminate; therefore as often. My first mathematical analysis of the proof of correctness is simple. At the the average behavior of Example 8 indicated conclusion of the outer loop we know that that the asymptotic number of comparisons A[m] ... A[i-1] and A[j] are not large, that and exchanges would be the same, even A[i] and A[j+ 1] ... A[n] are not small, and though the partitioned subfiles included all thatm v" in place of "A[j] is large" breaks down: the elements of the two subfiles after partitioning by Example 8 are not in in the above program. This gives a very random order! This was a surprise, because pretty algorithm, which is essentially equiva- randomness is preserved by the usual Quick- lent to the method published by Hoare [38] sort routine. When the N keys are distinct, in his first major application of the idea of v will be the largest element in the left subtile, invariants, and discussed in his original and the mechanism of Example 8 shows paper on Quicksort [36]. Note that since that v will tend to be near the left of that v = A[n], we know that the first execution subtile. When that subtile is later partitioned, of "loop while A[j] > v" will be trivial; it is highly likely that v will move to the we could move this loop to the end of the extreme right of the resulting right sub- outer loop just before the final repeat. This subtile. So that right sub-subtile will be would be slightly faster, but it would make subject to a trivial partitioning by its largest the program harder to understand, so I element; we have a subtle loss of efficiency would hesitate to do it. on the third level of recursion. I still haven't The Quicksort partitioning algorithm been able to analyze Example 8, but empiri- actually given in my book [54] is better than cal tests have borne out my prediction that Example 7a, but somewhat different from it is in fact about 15 % slower than the book the program we have just derived. My algorithm. version can be expressed as follows (assum- Therefore, there is no reason for anybody ing that A[m-1] is defined and

Computing Surveys, Vol. 8, No. 4, December197t Structured Programming with go to ,Statements • 289 though it is slightly cleaner looking than the Each of these programs leads to a Qnick- method in my book, it is noticeably slower, sort routine that makes about 102~N In N and we have nothing to fear by using a memory references on the average; the slightly more complicated method once it former is preferable (except on machines has been proved correct. Beautiful algo- for which exchanges are clumsy), since it is rithms are, unfortunately, not always the easier to understand. Thus I learned again most useful. that I should always keep looking for im- This is not the end of the Quicksort provements, even when I have a satisfactory story (although I almost wish it was, since program. I think the preceding paragraph makes an important point). After I had shown Ex- Axiomatics of Jumps ample 8 to my student, Robert Sedgewick, We have now discussed many different he found a way to modify it, preserving transformations on programs; and there are the randomness of the subfiles, thereby more which could have been mentioned (e.g., achieving both elegance and efficiency at the removal of trivial assignments as in [50, the same'time. Here is his revised program. exercise 1.1-3] or [54, exercise 5.2.1-33]). Example 8a: This should be enough to establish that a program-manipulation system will have i := m-l; j := n; v := A[n]; plenty to do. loop: Some of these transformations introduce loop: i := i%1; while A[i] < v repeat; go to statements that cannot be handled loop: j := j--l; while A[j] > v repeat; while i < j: very nicely by event:indicators, and in A[il := : A[jl; general we might expect to find a few pro- repeat; grams in which go to statements survive. A[i] := : A[n]; Is it really a formidable job to understand (As in the previous example, we assume such programs? Fortunately this is not an that Aim-1] is defined and < A[n], since insurmountable task, as recent work has the j pointer might run off the left end.) shown. For many years,: the go to ~tatement At the beginning of the outer loop the in- has been troublesome in the definition of variant conditions are now correctness proofs and language semantics; for example, Hoare and Wirth have pre- m--l _< i < j < n; sented an axiomatic definition of PASCAL A[k] < vform-l_< k < i; A[k] > v for j _< k < n; [41] in which everything but real arithmetic A[n] = v. and the go to is defined formally. Clint and Hoare [14] have shown how to extend this It follows that Example 8a ends with to event-indicator go to's (i.e., those which A[m]...A[i-1] < v = A[i] _< A[i+I]...A[n] don't lead into iterations or conditionals), but they stressed that the general case and m < i < n; hence a valid partition has appears to be fraught with complications. been achieved. Just recently, however, Hoare has shown Sedgewick also found a way to improve that there is, in fact, a rather simple way the inner loop of the algorithm from my to give an axiomatic definition of go to book, namely: statements; indeed, he wishes quite frankly i := m--l; j := n; v := A[n]; that it hadn't been quite so simple. For each loop: label L in a program, the programmer should loop: i := iq-1; while A[i] < v repeat; state a logical assertion a(L) which is to be A[j] := A[i]: true whenever we reach L. Then the axioms loop: j := j--l; while h[j] > v repeat; while i < j: {a(L)} go to L {false} A[il := A[j]; repeat; plus the rules of inference if i ~ j then j := j~l; A[j] := v; {~(L)} S{P} t- {a(L)} L:S {P}

Computing Surveys, Vol. 6, No. 4, December 1974 290 • DonaldE. Knuth are allowed in program proofs, and all and jump on overflow might be properties of labels and go to's will follow if overflow if the a(L) are selected intelligently. One then overflow := false; go to jump; must, of course, carry out the entire proof else go to no op; using the same assertion a(L) for each fi; appearance of the label L, and some choices I still believe that this is the correct way to of assertions will lead to more powerful write such a program. results than others. Such situations aren't restricted to in- Informally, a(L) represents the desired terpreters and simulators, although the state of affairs at label L; this definition foregoing is a particularly dramatic example. says essentially that a program is correct if Multiway branching is an important pro- a(L) holds at L and before all "go to L" gramming technique which is all too often statements, and that control never "falls replaced by an inefficient sequence of if through" a go to statement to the following tests. Peter Naur recently wrote me that he text. Stating the assertions a(L) is analogous considers the use of tables to control program to formulating loop invariants. Thus, it is flow as a basic idea of computer science that not difficult to deal formally with tortuous has been nearly forgotten; but he expects it program structure if it turns out to be will be ripe for rediscovery any day now. It necessary; all we need to know is the "mean- is the key to efficiency in all the best; corn- ing" of each label. priers I have studied. Some hints of this situation, where one Reduction of Complication problem reduces to another, have occurred There is one remaining use of go to for in previous examples of this paper. Thus, which I have never seen a good replacement, after searching for x and discovering that and in fact it's a situation where I still it is absent, the "not found" routine can think go to is the right idea. This situation insert x into the table, thereby reducing the typically occurs after a program has made a problem to the "found" case. Consider also multiway branch to a rather large number our decision-table Example 4, and suppose of different but related cases. A little com- that each period was to be followed by a putation often suffices to reduce one case to carriage return instead of by an extra space. another; and when we've reduced one problem Then it would be natural to reduce the to a simpler one, the most natural thing is post-processing of periods to the return- for our program to go to the routine which carriage part of the program. In each case, a solves the simpler problem. go to would be easy to understand. For example, consider writing an interpre- If we need to find a way to do this without tive routine (e.g., a microprogrammed saying go to, we could extend Zahn's event emulator), or a simulator of another com- indicator scheme so that some events are puter. After decoding the address and fetch- allowed to happen in the then.., fl part ing the operand from memory, we do a after we have begun to process other events. multiway branch based on the operation This accommodates the above-mentioned code. Let's say the operations include no-op, examples very nicely; but of course it can add, subtract, jump on overflow, and uncon- be dangerous when misused, since it gives us ditional jump. Then the subtract routine back all the power of go to. A restriction might be which allows (statement list)~ to refer to (event)j only for j > i would be less dan- operand := -- operand; go to add; gerous. With such a language feature, we can't the add routine might be "fall through" a label (i.e., an event indi- accum := accum -b operand; cator) when the end of the preceding code tyme := tyme ...I- 1; is reached; we must explicitly name each go to no op; event when we go to its routine. ProI~fibiting

Computing Surveys, Vol. 6, No. 4, December 1974 Structured Programming with go to Statements • 291

"fall through" means forcing a programmer in the same way by different people. Every- to write "go to common" just before the body knows it is a Good Thing, but as label "common:" in Example 7a; surpris- McCracken [64] has said, "Few people ingly, such a change actually makes that would venture a definition. In fact, it is not program more readable, since it makes the clear that there exists a simple definition as symmetry plain. Also, the program fragment yet." Only one thing is really clear: Struc- tured programming is not the process of subtract: operand := - operand; go to add; add: accum := accum + operand; writing programs and then eliminating their go to statements. We should be able to seems to be more readable than if "go to define structured programming without add" were deleted. It is interesting to referring to go to statements at all; then ponder why this is so. the fact that go to statements rarely need to be introduced as we write programs should follow as a corollary. 3. CONCLUSIONS Indeed, Dijkstra's original article [25] which gave Structured Programming its This has been a long discussion, and very name never mentions go to statements at detailed, but a few points stand out. First, all; he directed attention to the critical there are several kinds of programming question, "For what program structures can situations in which go to statements are we give correctness proofs without undue harmless, even desirable, if we are program- labor, even if the programs get large?" By ming in ALGOL or PL/I. But secondly, new correctness proofs he explained that he does types of syntax are being developed that not mean formal derivations from axioms, provide good substitutes for these harmless he means any sort of proof (formal or in- go to's, and without encouraging a pro- formal) that is "sufficiently convincing"; grammer to create "logical spaghetti". and a proof really means an understanding. One thing we haven't spelled out clearly, By program structure he means data struc- however, is what makes some go to's bad ture as well as contro[strueture. and others acceptable. The reason is that We understand complex things by sys- we've really been directing our attentior~ to tematically breaking them into successively the wrong issue, to the objective question simpler parts and understanding how these of go to elimination instead of the important parts fit together locally. Thus, we have subjective question of program structure. different levels of understanding, and each In the words of John Brown [9], "The act of of these levels corresponds to an abstraction focusing our mightiest intellectual resources of the detail at the level it is composed from. on the elusive goal of go to-less programs For example, at one level of abstraction, we has helped us get our minds off all those deal with an integer without considering really tough and possibly unresolvable whether it is represented in binary notation problems and issues with which today's or two's complement, etc., while at deeper professional programmer would otherwise levels this representation may be important. have to grapple." By writing this long At more abstract levels the precise value of article I don't want to add fuel to the con- the integer is not important except as it troversy about go to elimination, since that relates to other data. topic has already assumed entirely too much Charles L. Baker mentioned this principle significance; my goal is to lay that contro- as early as 1957, as part of his 8-page review versy to rest, and to help direct the discus- [2] of McCracken's first book on program- sion towards more fruitful channels. ming: Structured Programming Break the problem into small, self-contained , trying at all times to isolate the The real issue is structured programming, various sections of coding as much as possible but unfortunately this has become a catch ... [then] the problem is reduced to many phrase whose meaning is rarely understood much smaller ones. The truth of this seems

Computing Surveys, Vol. 6, No. 4, December 19'I4

~~~ ~ ..... 292 • Donald E. Knuth

very obvious to experienced coders, yet it is From these remarks it is clear that se- hard to put across to the newcomer. quential composition, iteration, and condi- tional statements present syntactic struc- Abstraction is easily understood in terms tures that the eye can readily assimilate; of BNF notation. A metalinguistic category but a go to statement does not. The visual like (assignment statement) is an abstrac- structure of go to statements is like that of tion which is composed of two abstractions flowcharts, except reduced to one dimension (a (left part list) and an (arithmetic expres- in our source languages. In two dimensions sion)), each of which is composed of abstrac- it is possible to perceive go to structure in tions such as (identifier) or (term), etc. We small examples, but we rapidly lose our understand the program syntax as a whole ability to understand larger and larger by knowing the structural details that relate flowcharts; some intermediate levels of these abstract parts. The most difficult abstraction are necessary. As an under- things to understand about a program's graduate, in 1959, I published an octopus syntax are the identifiers, since their meaning flowchart which I sincerely hope is the most is passed across several levels of structure. horribly complicated that will ever appear in If all identifiers of an ALGOL program wer~ print; anyone who believes that flowcharts changed to random meaningless strings of are the best way to understand a program symbols, we would have great difficulty is urged to look at this example [49]. (See seeing what the type of a variable is and also [32, p. 54] for a nice illustration of how what the program means, but we would go to's make a PL/I program obscure, and still easily recognize the more local features, see R. Lawrence Clark's hilarious spoof such as assignment statements, expressions, about linear representation of flowcharts by subscripts, etc. (This inability for our eyes means of a "come from statement" [13].) to associate a type or mode with an identifier I have felt for a long time that a t~dent has led to what I believe are fundamental for programming consists largely of the errors of human engineering in the design ability to switch readily from microscopic of ALGOL 68, but that's another story. My to macroscopic views of things, i.e., to change own notation for stacks in Example 6e levels of abstraction fluently. I mentioned suffers from the same problem; it works in this [55] to Dijkstra, and he replied [29] these examples chiefly because t is lower with an excellent analysis of the situation: case and S is upper case.) Larger nested structures are harder for the eye to see unless I feel somewhat guilty when I have suggested that the distinction or introduction of "differ- they are indented, but indentation makes the ent levels of abstraction" allow you to think structure plain. about only one level at a time, ignoring com- It would probably be still better if we pletely the other levels. This is not true. You changed our source language concept so that are trying to organize your thoughts; that is, the program wouldn't appear as one long you are seeking to arrange matters in such a way that you can concentrate on some portion, string. John McCarthy says "I find it diffi- say with 90% of your conscious thinking, while cult to believe that whenever I see a tree I the rest is temporarily moved away somewhat am really seeing a string of symbols." In- towards the background of your mind. But stead, we should give meaningful names to that is something quite different from "ignor- the larger constructs in our program that ing completely": you allow yourself tem- porarily to ignore details, but some overall correspond to meaningful levels of abstrac- appreciation of what is supposed to be or to tion, and we should define those levels of come there continues to play a vital role. You abstraction in one place, and merely use remain alert for little red lamps that suddenly their names (instead of including the de- start flickering in the corners of your eye. tailed code) when they are used to build I asked ttoare for a short definition of larger concepts. Procedure names do this, structured programming, and he replied that but the language could easily be designed it is "the systematic use of abstraction to so that no action of calling a is control a mass of detail, and also a means of implied. documentation which aids program design."

Computing Surveys, Vol. 6, No. 4, December 1974 Structured Programming with go to ~Stah~ments • 293

I hope that my remarks above have made state an overall purpose, for the statement the abstract concept of abstraction clear; as a whole. ! the second part of Hoare's definition (which We also need well-structured data; i.e., was also stressed by Dijkstra in his original as we write the program we should have an paper [25]) states that a good way to express abstract idea of what each variable means. the abstract properties of an unwritten piece This idea is also usually describable as an of program often helps us to write that invariant relation, e.g.,: "m is the number of program, and to "know" that it is correct items in the table" or "x is the search argu- as we write it. ment" Or "L[t] is the number of the root Syntactic structure is just one part of the node of node t's left subtree, or 0 if this picture, and BNF would be worthless if the subtree is empty" or "the contents of stack syntactic constructs did not correspond to S are postponed obligations to do such and semantic abstractions. Similarly, a good such". program will be composed in such a way Now let's consider the slightly more that each semantic level of abstraction has a complex case of an event-driven construct. reasonably simple relation to its constituent This should also correspond to a meaningful parts. We noticed in our discussion of abstraction, and our examples show what is Jacopini's theorem that every program can involved: For each event we give an (in- trivially be expressed in terms of a simple variant) assertion which describes the situa- iteration which simulates a computer; but tion which must hold when that event that iteration has to carry the entire be- occurs, and for the loop until we also give havior of the program through the loop, so an invariant for the loop. An event statement it is worthless as a level of abstraction. typically corresponds to an abrupt change An iteration statement should have a in conditions so that a different assertion purpose that is reasonably easy to state; from the loop invariant is necessary. typically, this purpose is to make a certain An error exit can be considered well- Boolean relation true while maintaining a structured for precisely this \reason--it certain invariant condition satisfied by the corresponds to a situation that is~impossible variables. The Boolean condition is stated according to the local invariant assertions; in the program, while the invariant should it is easiest to formulate assertions that be stated in a comment, unless it is easily assume nothing will go ~ong, rather than supplied by the reader. For example, the to make the invariants cover all contin- invariant in Example 1 is that A[k] ~ x for gencies. When we jump out to an error exit 1 ~ /~ ~ i, and in Example 2 it is the same, we go to another level of abstraction having plus the additional relation Aim-k 1] = x. different assumptions. Both of these are so obvious that I didn't As another simple example, consider bi- bother to mention them; but in Examples nary search in an ordered array using the 6e and 8, I stated the more complicated invariant relation A[i] < x < A[j]: invariants that arose. In each of those cases loop while i~l < j; the program almost wrote itself once the k := (i+j) + 2; proper invariant was given. Note that an if A[k] < x then i :ffi k; "invariant assertion" actually does vary else ifA[k] > x then j :ffi k; slightly as we execute statements of the]oop, else cannot preserve the invariant fi; but it comes back to its original form when fi; repeat; we repeat the loop. Thus, an iteration makes a good abstrac- Upon normal exit from this loop, the tion if we can assign a meaningful invariant conditions i-bl ~ j and A[i] < x < A[3] describing the local states of affairs as it imply that A[i] < x < A[i-kl], i.e., that x executes, and if we can describe its purpose is not present. If the program comes to (e.g., to change one state to another). Simi- "cannot preserve the iinvariant" (because larly, an if.-- then -.- else -.- tl state- x = A[k]), it wants to go to another set of ment will be a good abstraction if we can assumptions. The event-driven construct

Computing Surveys ~ol. 6, No. 4, December 1974 294 • Donald E. Knuth

provides a level at which it is appropriate [24] used three go to statements, all of to specify the other assumptions. which were perfectly easy to understand; Another good illustration occurs in Ex- and I think at most two of these would ample 6g; the purpose of the main if state- have disappeared from his code if ALGOL 60 ment is to find the first node whose A value had had a while statement. But go to is should be printed. If there is no such t, the hardly ever the best alternative now, since event "finished" has clearly occurred; it is better language features are appearing. If better to regard the if statement as having the invariant for a label is closely related to the stated abstract purpose without con- another invariant, we can usually save com- sidering that t might not exist. plexity by combining those two into one abstraction, using something other than With go to Statements go to for the combination. We can also consider go to statements from There is also another problem, namely at the same point of view; when do they cor- what level of abstraction should we introduce respond to a good abstraction? We've al- a label? This however is like the analogous ready mentioned that go to's do not have a problem for variables, and the general an- syntactic structure that the eye can grasp swer is still unclear in both cases. Aspects automatically; but in this respect they are of data structure are often postponed, but no worse off than variables and other iden- sometimes variables are defined and passed tifiers. When these are given a meaningful as "parameters" to other levels of abstrac- name corresponding to the abstraction tion. There seems to be no clearcut idea as (N.B. not a numeric label!), we need not yet about a set of syntax conventions, relat- apologize for the lack of syntactic structure. ing to the definition of variables, which And the appropriate abstraction itself is an would be most appropriate to structured invariant essentially like the assertions programming methodology; but for each specified for an event. particular problem there seems to be an In other words, we can indeed consider appropriate level. go to statements as part of systematic ab- straction; all we need is a clearcut notion of Efficiency exactly what it means to go to each label. In our previous discussion we concluded that This should come as no great surprise. After premature emphasis on efficiency is a big all, a lot of computer programs have been mistake which may well be the source of written using go to statements during the most programming complexity and grief. last 25 years, and these programs haven't We should ordinarily keep efficiency con- all been failures! Some programmers have siderations in the background when we for- clearly been able to master structure and mulate our programs. We need to be sub- exploit it; not as consistently, perhaps, as in consciously aware of the data processing modern-day structured programming, but tools available to us, but we should strive not inflexibly either. By now, many people most of all for a program that is easy to who have never had any special difficulty understand and almost sure to work. (Most writing correct programs have naturally programs are probably only run once; and been somewhat upset after being branded I suppose in such cases we needn't be too as sinners, especially when they know per- fussy about even the structure, much less fectly well what they're doing; so they have the efficiency, as long as we are happy with understandably been less than enthusiastic the answers.) about "structured programming" as it has When efficiencies do matter, however, the been advertised to them. good news is that usually only a very small My. feeling is that it's certainly possible fraction of the code is significantly involved. to write well-structured programs with go to And when it is desirable to sacrifice clarity statements. For example, Dijkstra's 1965 for efficiency, we have seen that it is possible program about concurrent process control to produce reliable programs that can be

Computing Surveys, Vol. 6, No. 4, December 1974 Structured Programming with go to Sta~ments • 295 maintained over a period of time, if we start The Future with a well-structured program and then use It seems clear that lan~ages somewhat well-understood transformations that can be different from those in existence today applied mechanically. We shouldn't attempt would enhance the preparation of structured to understand the resulting program as it programs. We will perhaps eventually be appears in its final form; it should be thought writing only small modules which are iden- of as the result of the original program modi- tified by name as they are used to build fied by specified transformations. We can larger ones, so that devices like indentation, envision program manipulation systems rather than delimiters, might become feasible which will facilitate making and document- for expressing local structure in the source ing these transformations. language. (See the discussion following In this regard I would like to quote some Landin's paper [59].) Although our examples observations made recently by Pierre-Arnoul don't indicate this, it turns out that a given de Marneffe [19]: level of abstraction often involves several In civil engineering design, it is presently a related routines and data definitions; for mandatory concept known as the "Shanley example, when we decide to represent a table Design Criterion" to collect several functions in a certain way, we simultaneously want to into one part... If you make a cross-section specify the routines for storing and fetching of, for instance, the German V-2, you find ex- ternal skin, structural rods, tank wall, etc. If information from that table. The next gep- you cut across the Saturn-B moon rocket, you eration of languages will probably take into find only an external skin which is at the same account such related routines. time a structural component and the tank Program manipulation systems appea~ to wall. Rocketry engineers have used the "Shan- be a promising future tool which will help ley Principle" thoroughly when they use the fuel pressure inside the tank to improve the programmers to improve their programs, and rigidity of the external skin! . . . People can to enjoy doing it. Standard operating pro- argue that structured programs, even if they cedure nowadays is usually to hand code work correctly, will look like laboratory critical portions of a routine in assembly prototypes where you can discern all the indi- vidual components, but which are not daily language. Let us hope such assemblers will usable. Building "integrated" products is an die out, and we will see several levels of engineering principle as valuable as structur- language instead: At the highest levels we ing the design process. will be able to write abstract programs, while He goes on to describe plans for a prototype at the lowest levels we will be able to control system that will automatically assemble storage and register allocation, and to sup- integrated programs from well-structured press subscript range checking, etc. With an ones that have been written top-down by integrated system it will be possible to do stepwise refinement. debugging and analysis of the transformed Today's hardware designers certainly program using a higher level language for know the advantages of integrated cir- communication. All levels will, of course, cuitry, but of course they must first under- exhibit program structure syntactically so stand the separate circuits before the inte- that our eyes can grasp it. gration is done. The V-2 rocket would never I guess the big question, although it really have been airborne if its designers had orig- shouldn't be so big, is whether or not the inally tried to combine all its functions. ultimate language will have go to statements Engineering has two phases, structuring and in its higher levels, or whether go to will be integration; we ought not to forget either confined to lower levels. I personally one, but it is best to hold off the integration wouldn't mind having go to in the highest phase until a well-structured prototype is level, just in case I really need it; but I prob- working and understood. As stated by Wein- ably would never use it, if the general berg [93], the former regimen of analysis/ iteration and event constructs suggested in coding/debugging should be replaced by this paper were present. As soon as people analysis/coding/debugging/improving. learn to apply principles of abstraction

Computing Surveys, V01. 6, No. 4, December 1974 296 • Donald E. Knuth

consciously, they won't see the need for go sation, and four computer listings: to, and the issue will just fade away. On the Frances E. Mien Ralph L. London other hand, W. W. Peterson told me about Forest Baskett Zohar Manna his experience teaching PL/I to beginning G. V. Bochmann W. M. McKeeman programmers: He taught them to use go to Harlan D. Mills R. M. Burstall Peter Naur only in unusual special cases where if and Vinton Cerf Kjell Overholt while aren't right, but he found [78] that T. E. Cheatham, Jr. James Pe~erson "A disturbingly large percentage of the John Cocke W. Wesley Peterson students ran into situations that require Ole-Johan Dahl Mark Rain go to's, and sure enough, it was often because Peter J. Denning John Reynolds Edsger Dijkstra Barry K. Rosen while didn't work well to their plan, but James Eve E. Satterthwaite, Jr. almost invariably because their plan was K. Friedenbach D. V. Schorre poorly thought out." Because of arguments Donald I. Good Jacob T. Schwartz like this, I'd say we should, indeed, abolish Ralph E. Gorin Richard L. Sites Leo Guibas Richard Sweet go to from the high-level language, at least C. A. R. Hoare Robert D. Tennent as an experiment in training people to Martin Hopkins Niklaus Wirth formulate their abstractions more carefully. James J. Homing M. Woodger This does have a beneficial effect on style, B. M. Leavenworth William A. Wulf although I would not make such a prohibi- Henry F. Ledgard Charles T. Zaha tion if the new language features described These people unselfishly devoted hundreds of above were not available. The question is man-hours to helping me revise the firstdraft; and whether we should ban it, or educate against I'm sorry that I wasn't able to reconcile all of their it; should we attempt to legislate program interesting points of view. In many places I have shamelessly used their suggestions without an morality? In this case I vote for legislation, explicit acknowledgment; this article is virtually with appropriate legal substitutes in place a joint paper with 30 to 40 co-authors! However, of the former overwhelming temptations. any mistakes it contains are my own. A great deal of research must be done if we're going to have the desired language by APPENDIX 1984. Control structure is merely one simple issue, compared to questions of abstract data In order to make some quantitative esti- structure. It will be a major problem to keep mates of efficiency, I have counted memory the total number of language features within references for data and instructions, assum- tight limits. And we must especially look at ing a multiregister computer without cache problems of input/output and data for- memory. Thus, each instruction costs one matting, in order to provide a viable alterna- unit, plus another if it refers to memory; tive to CoBoL. small constants and base addresses are as- sumed to be either part of the instruction or ACKNOWLEDGMENTS present in a register. Here are the code se- quences developed for the first two examples, I've benefited from a truly extraordinary amount of help while preparing this paper. The individuals assuming that a typical assembly-language named provided me with a total of 144 pages of programmer or a very good optimizing com- single-spaced comments, plus six hours of conver- piler is at work.

Computing Surveys, Vol. 6, No. 4, December 1974 Structured Programming with go to 81z~ ~nent~ 297

LABEL INSTRUCTION COST TIMES

Example 1: rl ~-- 1 1 1 r2 ~m 2 1 r3 ~-x 2 1 to test 1 1 loop: A[rl]: r3 2 n-a to found if = 1 n-a rl ~- rl+l 1 n-1 test: rl : r2 1 n to loop if _4 1 n notfound: m +-rl 2 A[rl] ~-- r3 2 a B[rl] ~-- 0 2 a found: r4 ~-- B[rl] 2 1 r4 ~-- r4+l 1 1 B[rl] *- r4 2 1

LABEL INSTRUCTION COST TIMES

Example 2: r2 ~-- m 2 I r3 ~--x 2 1 A[r2+l] ~-- r3 2 1 rl ~--0 1 1 loop: rl ~-- rl+l 1 n A[rl]: r3 2 n to loop if ~ 1 n rl : r2 1 1 to found if < 1 1 notfound: m ~-- rl etc. as in Example I.

Computing Surveys, V01. 6, No. 4, Dee,ember 1974 298 * Donald E. Knuth

A traditional "90% efficient compiler" would render the first example as follows:

LABEL INSTRUCTION COST TIMES

Example 1: rl ~-- 1 1 1 to test 1 1 iner: rl ¢- i 2 n--1 rl ~-- r1+1 1 n--1 test: rl : m 2 n to notfound if ~ 1 n i ¢-- rl 2 n-a r2 ~-- A[rl] 2 nn-a r2: x 2 n-a to found if -- 1 n-a to iner 1 n- 1 notfound: rl ~-- m 2 a rl ~-- rl T 1 1 a i 4--rl 2 a mc-rl 2 a rl ~--x 2 a r2¢-i 2 a A[r2] ~-- rl 2 a Bit2] ~-- 0 2 a found: rl ~-- i 2 1 r2 ~-- B[rl] 2 1 r2 ~- r2W1 1 1 B[rl] ~- r2 2 1

4t Answer to PL/I Problem, page 267. 4t The variable I is increased before FOUND is tested. One way to fix 4b ~t the program is to insert "I = I - FOUND;" before the last state- * ment. * 46 4$

BIBLIOGRAPHY [5] BAUER/ F . L . "A-hi"p Iosophy" of program- ruing,' University of London Special Lec- tures in Computer Science (October 1973): [1] ASHCROFT, EDWARD, AND MANNA, ZOHAR. Lecture notes published by Math. Inst., "The translation of 'go to' programs to Tech. Univ. of Munich, Germany. 'while' programs," Proc. IFIP Congress [6] BERRY, DANIELM. "Loops with normal and 1971 Vol. 1, North-Holland Publ. Co., Am- abnormal exits," Modeling and Measure- sterdam, The Netherlands, 1972, 250-255. ment Note ~8, Computer Science Depart- [2] BAKER, CHARLES L. "Review of D. D. Mc- ment, Univ. California, Los Angeles, Calif. Cracken, Digital co~rputer programming," 1974, 39 pp. Math. Comput. 11 (19M), 298-305. [7] BOCHMANN, G.V. Multiple ex'ts from a [3] BAKER, F. TERRY, AND MILLS, HARLAN D. loop without the GOTO," Comm. ACM 16, "Chief programmer teams," Datamation 19, 7 (July 1973), 443-444. 12 (December 1973), 58-61. [8] B~HM, CORRADO AND JACOPINI, GUISEPPE. [4] BARRON, D. W. Recursive techniques in "Flow-diagrams, Turing machines, and programming, American Elsevier, New languages with only two formation rules," York, 1968, 64 pp. Comm. ACM 9, 5 (May 1966), 366-371.

Computing Surveys, Vol. 6, No. 4, December 1974 Structured Pregramming with go to Statements • 299

[9] BROWN,JOHN R. "In memoriam...", un- guages, C. Boon [Ed]., ~.nfoteeh State of the published note, January 1974. Art Report 7, 1972, 217~232. [10] BRUNO J., AND STIEGLITZ, K. "The expres- [29] DIJKSTRA,E. W. persbnal communication, sion of algorithms by charts," J. ACM 19, January 3, 1973. 3 (July 1972), 517-525. [30] DIJKSTRA,E. W. personal communication, [11] BURKHARD,W. A. "Nonrecursive tree tra- November 19, 1973. versal algorithms," in Proc. 7th Annual [31] DIJKSTRA, E. W. personal communication, Princeton Conf. on Information Sciences and • January 30, 1974. Systems, Princeton Univ. Press, Princeton, [32] DONALDSON, JAMES'R. "Structured pro- N.J., 1973, 403-405. gramming," Datamation 19, 12 (December [12] CHEATHAM,T. E., JR., AND WEGBREIT, BEN. 1973), 52-54. "A laboratory for the study of automating [33] DYLAN, Bos. Blonde on blonde, reeord album programming," in Proc. AFIPS 1972 Spring produced by Bob John~ston, Columbia Rec- Joint Computer Conf., Vol. 40, AFIPS Press, ords, New York, March 1966, Columbia C2S Montvale, N.J., 1972, 11-21. 841. [13] CLARK,R. LAWRENCE. "A linguistic contri- [34] GILL, STANLEY. "Automatic computing: Its bution to GOTO-less programming," Data- problems and prizes," Computer J. 8, 3 marion 19, 12 (December 1973), 62-63. (October 1965), 177-189. [14] GLINT,M., AND HOARE, C. A. R. "Program [35] HENDERSON, P. AND S~OWDON, R. "An ex- proving: jumps and functions," Acta Infor- periment in structured programming," matica 1, 3 (1972), 214-224. BIT 12, 1 (1972),,~8~-5~. ,, _ . , [15] COOPER, D. C. "The equivalence of certain [36] HOARE, C. A.R. Quicksort, Computer J. computations," Computer J. 9, 1 (May 5, 1 (1962), 10-15. 1966), 45-52. [37] HOARE, C. A. R. "An! axiomatic approach [16] COOPER, D. C. "BShm and Jacopini's re- to computer programming," Comm. ACM duction of flow charts," Comm. ACM 10, 8 12, 10 (October 1969!, 576-880, 583. (August 1967), 463, 473. [38] HOARE, C. A. R. 'Proof of a program: [17] DAHL,O.-J., DIJKSTRA, E. W., AND HOARE, FIND," Comm. ACM 14, 1 (January 1971), C. A. R. Structured programming, Academic 39-45. Press, London, England, 1972, 220 pp. [39] HOARE, C. A. R. "A note on the for state- [18] DARLINGTON,J., AND BURSTALL, R. M. "A ment," BIT 12, 3 (1972), 334-341. system which automatically improves pro- [40] HOARE, C. A. R. "Prospects for a better grams," in Proc. 8rd Interntl. Conf. on Arti- programming language," in High level lan- ficial Intelligence, Stanford Univ., Stanford, guages, C. Boon [Ed.], Infotech State of Calif., 1973, 479-485. the Art Report 7, 1972, 327-343. [19] DE MARNEFFE, PIERRE-ARNOUL. "Holon [41] HOARE, C. A. R., AND WXRTH, N. "An axio- programming: A survey," Universite de matic definition of thb programming lan- Liege, ~ervice Informatique, Liege, Bel- guage PASCAL," Ac~a ln~ormatiea 2, 4 gium, 1973, 135 pp. (1973), 335-355. i [20] DIJKSTRA, E. W. "Recursive program- [42] HOARE, C. A. R. "Hints for programming ming," Numerische Mathematik 2, 5 (1960), language design," COmputer Science re- 312-318. port STAN-CS-74-403, Stanford Univ., [21] DIJKSTRA,E. W. "Programming considered Stanford, Calif., January 1974, 29 pp. as a human activity," in Proc. IFIP Con- [43] HOPKINS, M~R~IN E: "Computer aided gress 1965, North-Holland Publ. Co., Am- ," in ~oftware engineering sterdam, The Netherlands, 1965, 213-217. techniques, J. N. Buxton and B. Randell [22] DIJKSTRA,E. W. "A constructive approach [Eds.] NATO Scientific Affairs Division, to the problem of program correctness," Brussels, Belgium, 1970, 99-101. BIT 8, 3 (1968), 174-186. [44] HOPKINS, MARTIN E, "A case for the [23] DIJKSTRA, E. W. "Go to statement con- GOTO," ProP. ACM Annual Conference sidered harmful," Comm. ACM ll, 3 (March Boston, Mass., August 1972, 787-790. 1968), 147-148, 538, 541. [45] HULL, T. E. "Would you believe structured [There are two instances of pages 147-148 FORTRAN?" SIGNUM Newsletter 8, 4 in this volume; the second 147-148 is rele- (October 1973), 13-16. ~ vant here.] [46] INGALLS, DAN. "The execution time pro- [24] DIJKSTRA,E. W. "Solution of a problem in file as a programming tool," in Compiler concurrent programming control," Comm. optimization, 2d Courant Computer Sci- ACM 9, 9 (September 1968), 569. ence Symposium, Randall Rustin [Ed.], [25] DIJKSTRA, E. W. "Structured program- Prentice-Hall, Englewood Cliffs, N. J., ming," in techniques, 1972, 107-128. J. N. Buxton and B. Randell [Eds.] NATO [471 KELLEY,ROBERT A., AND WAVrERS, JOHN Scientific Affairs Division, Brussels, Bel- R. "APLGOL-2, a structured programming gium, 1970, 84-88. system for APL," IBM Palo Alto Scientific [26] DIJKSTRA,E. W. "EWD316: A short intro- Center report 320-3318 i(August 1973), 29 pp. duction to the art of programming," Tech- [48] KLEENE, S. C. "Representation of events nical University Eindhoven, The Nether- in nerve nets," in Automata ~tudies, C. E. lands, August 1971, 97 pp. Shannon and J. McCarthy [Eds.],Princeton [27] DIJKSTRA, E. W. "The humble program- University Press, Princeton, N.J., 1956, 3- mer," Comm. ACM 15, 10 (October 1972), 40. 859-866. [49] KNUTH, DONALD E. "RUNCIBLE--Alge- [28] DIJKSTRA, E. W. "Prospects for a better braie translation on a limited computer," programming language," in High level lan- Comm. ACM 2, 11 (November, 1959), 18-21.

Computing Surveys, Vol.i 6, No. 4. December 1~q4 i 300 • Donald E. Knuth

[There is a bug in the flowchart. The arc [66] MILLAY, EDnA ST. VINCENT. "Elaine"; el. labeled "2" from the box labeled "0:" in Bartlett's Familiar Quotations. the upper left corner should go to the box [67] MILLER, EDWARD F., JR., AND LINDAMOOD, labeledR~ ffi 8003.] GEORGE E. "Structured programming: top- [50] KNUTH,DONALD E. Fundamental algorithms, down approach," Datamation 19, 12 (De- The art of computer programming, Vol. 1, cember 1973)i 55--57. Addison-Wesley, Reading, Mass. 1968 2d [68] MILLS, H. D~ "Top-down programming in ed., 1973, 634 pp • large systems," in Debugging techniques in [51] KNUTH, DONALD E. "An empirical study of large systems, Randall Rustin [Ed.], Pren- FORTRAN programs,!' Software--Practice tice-Hall, Englewood Cliffs, N. J., 1971, 41- and Experience 1, 2 (April-June 1971), 105- 55. 133. [69] MILLS, H. D. "Mathematical foundations [52] KNUTH, DONALDE., AND FLOYD, ROBERT W. for structured programming," report FSC "Notes on avoiding 'go to' statements," 72-6012, IBM Federal Systems Division, Information Processing Letters 1, 1 (Febru- Gaithersburg, Md. (February 1972), 62 pp. ary 1971), 23-31, 177. [70] MILLS, H. D, "How to write correct pro- [53] KNUTH, DONALD E. "George Forsythe and grams and know it," report FSC 73-5008, the development of Computer Science," IBM Federal Systems Division, Gaithers- Comm. ACM 15, 8 (August 1972), 721-726. burg, Md. (1973), 26 pp. [54] KNUTH, DONALD E. Sorting and searching, [71] NASSI, I. R., AND AKKOYUNLU, E. A. "Veri- The art of computer programming, Vol. 3, fication techniques for a hierarchy of con- Addison-Wesley, Reading, Mass., 1973, 722 trol structures," Tech. report 26, Dept. of Computer Science, State Univ. of New [55] ~NUTH, DONALD E. "A review of 'struc- York, Stony Brook, New York (January tured programming'," Stanford Computer 1974), 48 pp. Science Department report STAN-CS-73- [72] NAUR, PETER [Ed.] "Report on the al- 371, Stanford Univ., Stanford, Calif., June gorithmic language ALGOL 60," Comm. 1973, 25 pp- ACM 3, 5 (May 1960), 299-314. [56] KNUTH, DONALD E., AND SZWARCFITER, [73] NAUR,PETER. "Go to statements and good JAYME L. "A structured program to gener- Algol style," BIT 3, 3 (1963), 204-208. ate all topological sorting arrangements," [74] NAUR,PETER. "Program translation viewed Information Processing Letters 2, 6 (April as a general data processing problem," 1974) 153-157. Comm. ACM 9, 3 (March 1966), 176--179. [57] KOSARAJU,S. RAO. "Analysis of structured [75] NAUR, PETER. "An experiment on program rograms," Proe. Fifth Annual ACM Syrup. development," BIT 12, 3 (1972), 347-365. heory of Computing, (May 1973), 240-252; {76] PAGER, D. "Some notes on speeding up also in J. Computer and System Sciences, 9, certain loops by software, firmware, and 3 (December 1974). hard w are means, '~ in" Computers and auto- [58] LANDIN, P. J. "A correspondence between ALGOL 60 and Church's lambda-notation: mata, Jerome Fox lEd.f, John Wiley & Sons, part I," Comm. ACM 8, 2 (February 1965), 1yew xork 1972, 207-213; also in IEEE 89-101. Trans. Computers, C-21, 1 (January 1972), [59] LANDIN, P. J. "The next 700 programming 97-100. languages," Comm. ACM 9, 3 (March 1966), [77] PETERSON, W. W.; KASAMI, T.; AND TOK- 157-166. UEA, N. "On the capabilities of while, re- [60] LEAVENWORTH, B. M. "Programming peat, and exit statements," Comm. ACM with(out) the GOTO," Proc. ACM Annual 16, 8 (August 1973), 503-512. Conference, Boston, Mass., August 1972, 782- [78] PETERSON, W. WESLEY. personal communi- 786. cation, April 2, 1974. [61] MANNA, ZOHAR, AND WALDINGER, RICHARD [79] RAIN, MARK AND HOLAOER, PER. "The J. "Towards automatic program synthesis," present most recent final word about labels in Symposium on Semantics of Algorithmic in MARY," Machine Oriented Languages Bul- Languages, Lecture Notes in Mathematics letin 1, Trondheim, Norway (October 1972), 188, E. Engeler [Ed.], Springer-Verlag, New 18-26. York, 1971, 270-310. [80] REID, CONSTANCE. Hilbert, Springer-Verlag, [62] McCARTHY, JOHN. "Reeursive functions New York, 1970, 290 pp. of symbolic expressions and their compu- [81] REYNOLDS, JOHN. "Fundamentals of struc- tation by machine, part I," Comm. ACM 3, tured programming," Systems and Info. 4 (April 1960), 184-195. Set. 555 course notes, Syracuse Univ., Syra- [63] MCCARTHY,JOHN. "Towards a mathemati- cuse, N.Y., Spring 1973. cal science of computation," in Proc. IFIP [82] SATTERTHWAITE, E. H. "Debugging tools Congress 1962, Munich, Germany, North- for high level languages," Software--Practice Holland Publ. Co., Amsterdam, The Nether- and Experience 2, 3 (July-September 1972), lands, 1963, 21-28. 197-217. [64] McCRACKEN, DANIEL D. "Revolution in [83] SCHNECK, P. B., AND ANGEL, ELLINOR. "A rogranmaing," Datamation 19, 12 (Decem- FORTRAN to FORTRAN optimizing com- • er 1973), 50-52. piler," Computer J. 16, 4 (1973), 322-330. [65] McKEEMAN, W. M.; HORNING, J. J.; AND [84] SCHORRE, D. V. "META-II--a syntax-di- WORTMAN, D. B. A compiler generator, rected compiler writing language," Proc. Prentice-Hall, Englewood Cliffs, N. J., ACM National Conference, Philadelphia, 1970, 527 pp. Pa., 1964, paper D1.3.

Computing Surveys, Vol. 6, No. 4, December 197/4 Structured Programming with go to ,~tabynents • 801

[85] SCHORRE, D. V. "Improved organization improved prograr~ing: perf~mance," for procedural languages," Tech. memo Datamation 17, 11 (November 1972), 82--85. TM 3086/002/00, Systems Development [94] WIRTH, N. ¢'On certain basic concepts of Corp., Santa Monica, Calif., September 8, programming languages," Stanford Com- 1966, 8 pp. puter Science Report CS 65, Stanford, Calif. [86] SHIGO, O.; SHIMOMURA, T.; FUJIBAYASHI (May 1967), 30pp. S.; AND MAEJIMA, T. "SPOT: an experi- [95] WIRTH, N. "PL 360,i a programming lan- mental system for structured programming" guage for the 360 computers," J. ACM 15, (in Japanese), Conference Record, Informa- 1 (January 1968), 37-74. tion Processing Society of Japan, 1973. [96] WIRTH, N. "Prog,ra~ development by step- ]Translation available from the authors, wise refinement, Comm.ACM 14, 4 (April Nippon Electric Company Ltd., Kawa- 1971), 221-227. saki, Japan.] [97] WIRTH, N. "The programming language [87] STRACHEY, C. "Varieties of programming Pascal," Aeta Info~natica 1, 1 (1971), 35- language," in High level languages, C. Boon 63. lEd.], Infotech State of the Art Report 7, [98] WULF, W. A. ; RUSSELL, D. B. ; ~D HABER- 1972, 345-362. MANN, A. N. "BLISSi: A language for sys- [~] STRONG,H. R. JR. "Translating recursion tems programming,'~ Comm. ACM 14, 12 equations into flowcharts," J. Computer and (December 1971), 780~-790. System Sciences 5, 3 (June 1971), 254-285. [99] WULF, W. A. "Prog~nuning without the [89] TEITELMAN, W. "Toward a programming goto," Information ~Pror~ssing 71, Prec. laboratory," in Software Engineering Tech- IFIP Congress, Vol. 1, North-Holland niques, J. N. Buxton and B. Randall [Eds.], Publ. Co., Amsterdam, The Netherlands, NATO Scientific Affairs Division, Brussels, 1971, 408-413. Belgium, 1970, 137-149,~ N [100] WULF, W. A. "A case against the GOTO," [90] TEITELMAN,W. et al. I TERLISPreference Prec. ACM 197~ Annual Conference, Bos- manual," Xerox Pale Alto Research Center, ton, Mass. (August 1972), 791-797. Pale Alto, Calif., and Bolt Beranek and [101] WULF, W. A.; JOHNSON,RIeH~Rn K.; WEIN- Newman, Inc., 1974. STOCK, CHARLESP.; AND HeRBs, STEVEN O. [91] WALKER,S. A., AND STRONG, I-I. a. "Char- "The design of an 4ptimizing compiler," acterizations of flowchartable recursions," Computer Science !Department report, J. Computer and System Sciences 7, 4 (Au- Carnegie-Mellon Univ., Pittsburgh, Pa., gust 1973), 404-447. (December 1973), 103~pl~. [92] WEGNER,EEF.RHARD. "Tree-structured pro- [102] ZAHN, CHARLEST. "A control statement for grams," Comm. ACM 16, 11 (November natural top-down structured program- 1973), 704-705. ming," presented atl Symposium on Pro- [93] WEINBERG,GERALD M. "The psychology of gramming Languages~ Parrs, 1974.

Computing Surveys, VoL 6, No. 4, December 1974