Notes
{Note Omniscient Implementors}
One can argue quite strongly that there are so large a number (possibly infinite) of distinct useful control constructs that no one language could embody them all, and that therefore no language designer should be so conceited as to think he has encompassed all desirable constructions in a given language. By this reasoning, the omission of CASE
, or Dahl loops, or event constructions, or whatever else is not a matter of neglect, but of necessity: you just can't foresee them all.
(This brings out a serious flaw in the present theory of structured programming; by assuming that all programs can conveniently be written using only certain structures, it implicitly assumes that the problems to be solved by these programs have solutions which can be decomposed using these structures. We have never seen any justification advanced for this latter assumption; and indeed, there are many counterexamples, such as Yourdon's "finite-state machine" program mentioned in the text.)
Until a much more advanced theory of programming is devised, designers of practical languages are well advised to leave in "ugly hooks" like GOTO
, even if also discouraging their use except in emergencies. After all, using GOTO
to simulate a peculiar control construct is probably preferable to a convoluted perversion of a more specialized construct.
{Note Shuffle Arguments}
To elucidate this point further, suppose that function arguments are passed on the stack (above the return address). Then, using a true stack discipline plus tail-recursion, if there are intermediate results or other data above that return address, the arguments to be passed must be moved down over this other data so that they will be in the correct position. This is particularly tricky because these positions are probably also where the arguments passed to you were stored. For example, suppose A
calls B
, and B
calls C
tail-recursively. Then A
passes a return address R
and arguments to B
, and B
wishes to pass R
and different arguments to C
. B
must replace its arguments from A
with the new ones for C
. This entails some shuffling of the stack positions.
The need to shuffle stack positions can be alleviated by passing arguments through registers, but this in turn usually requires shuffling of registers. Another way out is to use a more general form of stack, such as the so-called "spaghetti" [Bob73] or "macaroni" [Ste77c] stacks. Under such a scheme there is no need to shuffle old arguments away so that new arguments to be passed may occupy their positions; instead, each stack frame has a pointer to the next one, and two stack frames may both point to a third. Thus B
would build a new stack frame F'
pointing to the one G
containing R
; B
's arguments remain in frame F
, which also points to G
. On calling C
, F'
is passed to C
and F
is discarded.
{Note Step Variables}
A far more important point not mentioned in the main text is that procedures not only can easily express the control structure of various kinds of loops, but also provide a natural way to express the stepping of the variables. Consider the loop for "iterative factorial" written in terms of a LISP LABEL
construct: