[comp.software-eng] gotos

friedl@vsi.UUCP (Stephen J. Friedl) (04/22/88)

In article <2200@louie.udel.EDU>, new@udel.EDU (Darren New) writes:
>
> [responding to the "give me something hard to translate" challenge]
>
> How about: (pseudocode)
>    for (i = 0; i < max_in_table && key != name[i]; ++i) 
>       if (keypressed()) goto handle_key;
>    seek(helpfile, offset[i]);
>    if (keypressed()) goto handle_key;
>
>	[more code with output and "keypressed" here.  Presumably
>	this lets redisplay stop and process the next command and
>	possibly avoiding wasted output.  Emacs and WordStar do
>	this, among others]
> 
> I contend that if the reading and formatting and displaying are sufficiently
> slow, such behaviour can be desirable (i.e., on microcomputers).
> I would like to see a goto-less version of this that is easier to understand.

Let's put this goto business in perspective:

	/*
 	 * strcpy() - written by John Doe
	 */
	strcpy(dst, src)
	char	dst[], src[];
	{
	int	i;

		for (i = 0; dst[i] = src[i]; i++)
			;
	}

     Who objects to this?

     Even a beginnermediate C programmer is likely to rewrite
this function using pointers, but it is unlikely that a
passionate hatred of [arrays] (or of John Doe) will develop as
well.  In this case, the use of arrays indicates lack of
experience or insight, not of evil intent or laziness.

       ***********************************************
       * The problem is not with arrays but with the *
       * underlying thought that caused their use.   *
       *                                             *
       * The solution is not to ban the array but to *
       * teach the insight.                          *
       ***********************************************

     And so it goes with our bedeviled goto.  In the quoted text
above, goto probably indicates a lack of experience with really
neat methods of preemptive redisplay.   Gosling Emacs does this
by checking for keypressed() *one place* very deeply in the
display handler rather than sprinkle these calls everywhere; Sys
V Rel 3 curses does it like this too.  I have oversimplified
it here, but it is extremely elegant and beautiful to behold.
Those attempting mere transliteration of the above fragment
are condemned to failure even if no gotos are used -- the failure
just won't be as "obvious".

       ***********************************************
       * The problem is not with the goto but with   *
       * the underlying thought that caused its use. *
       *                                             *
       * The solution is not to ban the goto but to  *
       * teach the insight.                          *
       ***********************************************

     The array example is a trivial one but (I believe) valid.
When you are learning C you will naturally stumble over the
syntax and semantics of the language, but it is part of learning.
In my opinion, *excessive* use of arrays tells me that the user
needs to get more comfortable with pointers, but I betcha this
person does not believe that they are doing anything "wrong".
Perhaps they are not: the program works, doesn't it?

     Once you've learned C you still have a long way to go.
There are many, many more levels of insight beyond just learning
syntax -- learning to speak Spanish won't make you a great
speaker in Mexico City.  The goto shows lack of insight on a much
higher level, and it is a lot harder to convince people that they
are missing that insight.  Showering users of gotos with $50 fines
and ridicule is hardly conducive to inspired thinking.

     The solution to this goto problem is not to be found by
banning the goto but by better fundamental understandings.
Focusing on language syntax is largely a wasted effort that
distracts from the real issues.

    C'mon guys/gals, let's replace:

 	"you show me a fragment with gotos and I can rewrite
 	it without them" (said with challenging tone)

with

 	"You show me what you want to accomplish, and I'll
 	suggest some alternate ideas" (said with helpful tone).

     If you use gotos a lot, ask a wizard for suggestions and try
to gain something beyond solving the problem at hand.  If you are
a wizard, share your experience as others undoubtedly did with
you.  Most of us really enjoy the fun of computers and want to be
the best we can be.  Let's help each other out.


     I've crossposted this to comp.software-eng in the hope
that some of the net.readers over there might have something
to offer from a software engineering viewpoint rather than a
language syntax viewpoint.  Anyone?

-- 
Steve Friedl       V-Systems, Inc.      Resident access(2) basher
friedl@vsi.com   {backbones}!vsi.com!friedl    attmail!vsi!friedl

g-rh@cca.CCA.COM (Richard Harter) (04/24/88)

	I suppose there is some justification for the recurring issue
of gotos -- it usually raises some interesting issues of coding technique.
It's not a profound topic, but it's fun for all concerned.  It seems to me
that the profit, if any, in these exercises is to analyze these little
difficulties and spot where the problem is and what a good resolution is.

	In the case in hand we have a very common task -- there are a
series of actions.  After each action we check to see if a certain thing
has happened -- if it has we cease stepping through the actions and go
on to the next stage.  The proposed techniques include

a)	Use goto's at each checkpoint which branch to a common label

b)	Fake it with a block and break statements

c)	Use nested if's

d)	Use a signal handler.

The signal handler approach is pretty, but it isn't general, and doesn't
really work right in this case.  The nested if's approach is, in my view,
worse than using goto's -- it buries the essential structure of the task
rather than explicates it.  Block and break is fine as long as it is con-
venient to put all the tests at the first level.  So far, however, nobody
has really made a good case for not using goto's for this kind of task,
as far as I can see, other than it is naughty to use goto's.  Here are
some more alternatives:

e)	Put the sequence of actions in a function and get out with return
	statements.  Much the same as block and break, except that you
	can do deep level escapes.

f)	Put the test condition in a while statement and step through the
	actions [somehow].  Unfortunately, I don't see a nice way to do
	this in C.  The thought that first occurs to one is something like

for (step=0,done=0;!done;step++) {
  switch(step) {
    case 0:  action_0;
             break;
    case 1:  action_1;
             break;
    ......
    default: done = 1;
             break;
    }
  if (!done && test_condition) done=1;
  }

which is all very well in its way (there is now only one expression of
the test condition) but does look remarkably clumsy and is a maintenance
problem (what happens when you want to insert a new action?).  Perhaps
someone else has a neater way to do it.

In assembly language this type of situation is a natural for threaded code.
The labels for the action sequences go on a stack.  After each action you
branch to the test code, which in turn pops the next action label.  All
very straightforward, but I don't really see a nice way to implement threaded
code in C without using function arrays.
-- 

In the fields of Hell where the grass grows high
Are the graves of dreams allowed to die.
	Richard Harter, SMDS  Inc.

cl@datlog.co.uk (Charles Lambert) (04/28/88)

In article <27310@cca.CCA.COM> g-rh@CCA.CCA.COM.UUCP (Richard Harter) writes:
>
>f)	Put the test condition in a while statement and step through the
>	actions [somehow].  Unfortunately, I don't see a nice way to do
>	this in C.  The thought that first occurs to one is something like
>
>for (step=0,done=0;!done;step++) {
>  switch(step) {
>    case 0:  action_0;
>             break;
>    case 1:  action_1;
>             break;
>    ......
>    default: done = 1;
>             break;
>    }
>  if (!done && test_condition) done=1;
>  }

This is almost a finite-state machine, and state machine programming is an
accepted idiom. Generalise it as follows:

state=STAT0;
while ( state != STATE_DONE ) {
    switch (state) {

    STATE0: action_0();
	    if (exit_condition_0)
		state = STATE_DONE;
	    else
		state=STATE1;
	    break;
    STATE1: action_1();
	    if (exit_condition_1)
		state = STATE_DONE;
	    else
		state = STATE2;
	    break;
    .
    .
    .
    }
}

Note that with this model,  states may occur in any order and even
repetitively.  The "if...goto" construct is simply a sequential case of the
general state machine.

-----------------
Charles Lambert