[net.lang.c] C style

dfm (10/26/82)

A rather radical difference of opinion here, so everybody start sharpening
your axes...  As has been pointed out, usual C practice makes braces very
visible.  I believe this is not a good idea.  The things which are made to
stand out should be the ones people need to see, not machines.  Braces
carry no information to the human reader -- he gets the nesting out of the
level of indentation (of course, I have no hard evidence to back this up,
so ...). The braces are just nasty gizmos the stupid compiler needs, so
tuck them out of the way at the end of a line, thus

	if (exp) {
		......;
		......;
		......; }
	else
		......;
	while (exp)
		......;
	while (exp) {
		......;
		......; }
	.......;

Note that contrary to a previous message you do not need braces lining up to
see where blocks begin and end.  You still draw a line down the page.  Another
advantage, to my obviously demented way of thinking, of this whole thing is
that there's less whitespace.  While lots of semantically blank lines and such
is nice on a big hard copy, I believe most of us are tied to nastly ~24 line
screens and need to see as much real information on one page as possible.

CSvax:cak (10/27/82)

Boy, I've been waiting for that one. I almost submitted it
myself, as a joke, except I was going to do

	if(...)
		{......
		.......
		.......}

I seem to recall that about four years ago, the rumour was floating
around that dmr was considering doing away with braces completely,
and have blocks be delimited solely by indentation. This was
almost universally decried because of the easy mistakes that
could be made. You are advocating almost the same thing.

I think it's necessary to see the braces.

What style do I prefer? Aw, come on, I'm smarter than that. I'm
not gonna tell.

Cheers,
chris

rlk@chinet.UUCP (Richard L. Klappal) (09/11/85)

The question:
	Which of the following code segments is more understandable,
	(readable, structured, etc) given the current attitudes
	about the presence of goto's in programming?

(For the sake of argument, assume I left the comments out of both
versions entirely.  I included them here in case people are not clear
what the various curses routines do.)


GOTO VERSION:
	. . .
	noecho();		/* turn off echo */
retry:
	move(4,10);		/* set cursor position */
	refresh();		/* update screen */
	ch = getch();		/* get input character (without echo) */
	if ((ch < '1' || ch > '5') && ch != 'E')
	{			/* valid input is 1 thru 5 or 'E' */
		putchar(BELL);	/* sound bell on invalid input */
		goto retry;
	}
	addch(ch);		/* echo the valid character */
	refresh();		/* update screen */
	. . .

versus NO GOTO VERSION

	for ( ; (((ch=getch()) < '1' || ch > '5') && ch != 'E') ; )
		putchar(BELL);
	addch(ch);
	refresh();



My background is predominately from a FORTRAN-BASIC-PL/I
environment, so I tend to think of the FOR (.;.;.){} in terms
of DO .. CONTINUE, iterative DO .. END or DO WHILE .. END constructs,
where this kind of an assignment in the conditional is verboten.

I guess what I'm really asking is:
	If you had to modify this program, written by someone else who
	commented it sparsely, which style would you prefer to work on?


-- 

Richard Klappal

UUCP:		..!ihnp4!chinet!uklpl!rlk  | "Money is truthful.  If a man
MCIMail:	rklappal		   | speaks of his honor, make him
Compuserve:	74106,1021		   | pay cash."
USPS:		1 S 299 Danby Street	   | 
		Villa Park IL 60181	   |	Lazarus Long 
TEL:		(312) 620-4988		   |	    (aka R. Heinlein)
-------------------------------------------------------------------------

foss@ihuxe.UUCP (foss) (09/13/85)

> 
> The question:
> 	Which of the following code segments is more understandable,
> 
> GOTO VERSION:
> 	. . .
> 	noecho();		/* turn off echo */
> retry:
> 	move(4,10);		/* set cursor position */
> 	refresh();		/* update screen */
> 	ch = getch();		/* get input character (without echo) */
> 	if ((ch < '1' || ch > '5') && ch != 'E')
> 	{			/* valid input is 1 thru 5 or 'E' */
> 		putchar(BELL);	/* sound bell on invalid input */
> 		goto retry;
> 	}
> 	addch(ch);		/* echo the valid character */
> 	refresh();		/* update screen */
> 	. . .
> 
> versus NO GOTO VERSION
> 
> 	for ( ; (((ch=getch()) < '1' || ch > '5') && ch != 'E') ; )
> 		putchar(BELL);
> 	addch(ch);
> 	refresh();
> 
----------------------------

Enable Flame Thrower:

It is a tie. Both versions are lame and don't use 
the most appropriate construct avaliable.  
	-> Goto's are for teen-agers who play video games 
	   (and grow up to play D & D). 
	-> With the 'for' loop, the general idea is:

	   for( initial condition; final condition; change condition )
		foo;

	   Unfortunately, the 'for' loop above has no 
	   initial condition and no change condition.
.............................

I suggest a more appropriate construct, the 'while' loop:

	while(( ch =  getch()) != EOF )
	{
		if(( ch < '1' || ch > '5') && ch != 'E')
			putchar( BELL );
		addch( ch );
		refresh();
	} /* end while */

This IS more readable and demonstrates what 
really needs to be done by the loop.

Disable Flame Thrower:

----------------------------------------------------------------
Raymond M. Foss -> AT&T Bell Laboratories -> ..!ihnp4!ihuxe!foss
----------------------------------------------------------------

brownc@utah-cs.UUCP (Eric C. Brown) (09/13/85)

In article <180@chinet.UUCP> rlk@chinet.UUCP (Richard L. Klappal) writes:
>	Which of the following code segments is more understandable,
>	(readable, structured, etc) given the current attitudes
>	about the presence of goto's in programming?
>

Well, neither.
how about
	for ( ch = getch(); ch < '1' || ch > '5') && ch != 'E'); ch = getch())
		putchar(BELL);
	addch(ch);
	refresh();
which ought to do the same thing.

Eric C. Brown
brownc@utah-cs
..!{decvax, ihnp4}!utah-cs!brownc

gwyn@brl-tgr.ARPA (Doug Gwyn <gwyn>) (09/13/85)

> 	Which of the following code segments is more understandable,
> 	(readable, structured, etc) given the current attitudes
> 	about the presence of goto's in programming?
> 
> GOTO VERSION:
> 	. . .
> 	noecho();		/* turn off echo */
> retry:
> 	move(4,10);		/* set cursor position */
> 	refresh();		/* update screen */
> 	ch = getch();		/* get input character (without echo) */
> 	if ((ch < '1' || ch > '5') && ch != 'E')
> 	{			/* valid input is 1 thru 5 or 'E' */
> 		putchar(BELL);	/* sound bell on invalid input */
> 		goto retry;
> 	}
> 	addch(ch);		/* echo the valid character */
> 	refresh();		/* update screen */
> 	. . .
> 
> versus NO GOTO VERSION
> 
> 	for ( ; (((ch=getch()) < '1' || ch > '5') && ch != 'E') ; )
> 		putchar(BELL);
> 	addch(ch);
> 	refresh();

There are several other non-GOTO ways to rewrite the first example:

	move( 4, 10 );			/* position cursor */
	refresh();

	valid = false;
	while ( !valid )
		switch ( ch = getch() )	/* validate input char */
			{
		case 'E':
		case '1':
		case '2':
		case '3':
		case '4':
		case '5':
			valid = true;
			break;

		default:		/* not valid */
			putchar( BELL );
			}

	addch( ch );			/* echo valid input char */
	refresh();

Although this introduces an additional (Boolean) variable, which is
one of the problems with goto-less programming, the variable has
very clear meaning and may actually help in understanding the code.

> I guess what I'm really asking is:
> 	If you had to modify this program, written by someone else who
> 	commented it sparsely, which style would you prefer to work on?

Mine, of course.

Actually, I would suggest packaging similar code into some more general
module, perhaps

char getvalid( Point where, const char *prompt, const char *valids );

The value of doing this should be clear, but if not we can get into
the reasons for it.

rlk@chinet.UUCP (Richard L. Klappal) (09/15/85)

In article <1217@ihuxe.UUCP> foss@ihuxe.UUCP (foss) writes:
>> 
>> The question:
>> 	Which of the following code segments is more understandable,
>> 
>> GOTO VERSION:
>>		[deleted example]
>> versus NO GOTO VERSION
>>		[deleted example]
>----------------------------
>
>Enable Flame Thrower:
>
>It is a tie. Both versions are lame and don't use 
>the most appropriate construct avaliable.  
>	-> Goto's are for teen-agers who play video games 
>	   (and grow up to play D & D). 
>	-> With the 'for' loop, the general idea is:
>
>	   for( initial condition; final condition; change condition )
>		foo;
>
>	   Unfortunately, the 'for' loop above has no 
>	   initial condition and no change condition.
>.............................
>
>I suggest a more appropriate construct, the 'while' loop:
>
>	while(( ch =  getch()) != EOF )
>	{
>		if(( ch < '1' || ch > '5') && ch != 'E')
>			putchar( BELL );
>		addch( ch );
>		refresh();
>	} /* end while */
>
>This IS more readable and demonstrates what 
>really needs to be done by the loop.
>
>Disable Flame Thrower:
>
>----------------------------------------------------------------
>Raymond M. Foss -> AT&T Bell Laboratories -> ..!ihnp4!ihuxe!foss
>----------------------------------------------------------------

I think you missed the point slightly, but answered the question
anyway.  Now that I stand back abit and look at your response,
I think
	while (((ch=getch()) < '1' || ch > '5') && ch != 'E')
		putchar(BELL);
would be the construct I wanted.

The intent is to read ONE character which MUST be a digit
between 1 and 5, inclusive, or an 'E'.  EOF would be invalid,
plus the fact that this package is intended for non-technical
types who wouldn't know CTRL D from their left ear (no insult
intended, but it's outside their realm of experience).

Thanks.  Sometimes I get too close so close to the tree that
I see plenty of bark, but no forest.


-- 

Richard Klappal

UUCP:		..!ihnp4!chinet!uklpl!rlk  | "Money is truthful.  If a man
MCIMail:	rklappal		   | speaks of his honor, make him
Compuserve:	74106,1021		   | pay cash."
USPS:		1 S 299 Danby Street	   | 
		Villa Park IL 60181	   |	Lazarus Long 
TEL:		(312) 620-4988		   |	    (aka R. Heinlein)
-------------------------------------------------------------------------

throopw@rtp47.UUCP (Wayne Throop) (09/15/85)

> The question:
>         Which of the following code segments is more understandable,
>         (readable, structured, etc) given the current attitudes
>         about the presence of goto's in programming?
>                       ...
>         If you had to modify this program, written by someone else who
>         commented it sparsely, which style would you prefer to work on?

I've left the (more or less) original two versions to the end of this
article.  I note that they don't do the same thing, and thus are not
valid comparisons.  I also note that a "for" is used where a "while"
should be used, causing the "non goto version" to be less comprehensible
than it should be.  My conclusion: I wouldn't want to have to modify
either of the original code segements.

In any event, here is a similar example pair:

    ...
    init_screen();
    for( read_command(); invalid_command(); read_command() )
        display_error();
    execute_command();
    ...

vs
    ...
    init_screen();
  retry:
    read_command();
    if( invalid_command() ){
        display_error();
        goto retry;
    }
    execute_command();
    ...

I prefer the first.  I am guaranteed that there is only one way to enter
this code fragment, and only one way to leave it (barring longjmp).  The
second fragment has no such assurance... it can be entered in the middle
from anywhere in the containing function.  Thus, the entire function
must be searched for references to the label just to be sure that the
conditions that must be met for the code that follows the label are met
everywhere there is a goto that label.

I tend to agree with the usual wisdom that setjmp, longjmp, goto, and
other labeled transfers of control are useful in error or exception
processing, and are pure obfuscation in almost every other case.  The
example was a sort of error recovery, but not one where the obfuscation
of a goto was waranted to bail out of the situation.


Note that the problem with gotos isn't the goto... it's the come-from.
That is, the code around the goto isn't unclear.  The code around the
*label* is unclear.  Thus, the C goto could be tamed somewhat if only
one could scope label reference appropriately.  For example, a lint-like
tool could allow only one label to appear in any compound statement, and
could allow gotos to that label only from within that compound statement
(and only from *before* the label).  This would assure that

    {   ...
             goto bailout;
        ...
    bailout:
        ...
    }

would be a one-entry one-exit structure, and go a long way to "taming"
the goto.  There are still many (potential) ways for control to reach
the "bailout" label, but it is clearer where to look for them.

Comments?


[Examples from original posting for reference.]
> GOTO VERSION:
>         . . .
>         noecho();
> retry:
>         move(4,10);
>         refresh();
>         ch = getch();
>         if ((ch < '1' || ch > '5') && ch != 'E')
>         {
>                 putchar(BELL);
>                 goto retry;
>         }
>         addch(ch);
>         refresh();
>         . . .
>
> versus NO GOTO VERSION
>
>         for ( ; (((ch=getch()) < '1' || ch > '5') && ch != 'E') ; )
>                 putchar(BELL);
>         addch(ch);
>         refresh();
-- 
Wayne Throop at Data General, RTP, NC
<the-known-world>!mcnc!rti-sel!rtp47!throopw

chris@umcp-cs.UUCP (Chris Torek) (09/15/85)

Style arguments tend to be unproductive, so I shall stay out of
this one.  However, let me point out that in ``fixing'' a program
one must be careful that the behaviour is not altered (unless that
is part of the fix).  Article <1217@ihuxe.UUCP> by foss@ihuxe.UUCP
suggests using a while loop, but the code fragment given does not
do what the original code in <180@chinet.UUCP> did.
-- 
In-Real-Life: Chris Torek, Univ of MD Comp Sci Dept (+1 301 454 4251)
UUCP:	seismo!umcp-cs!chris
CSNet:	chris@umcp-cs		ARPA:	chris@maryland

tombre@crin.UUCP (Karl Tombre) (09/16/85)

If I had to work on a program written by somebody else, I would
definitely prefer to work on the version WITHOUT GOTOs. I have
already had to update programs with GOTOs, and be sure that more
than 3 of them in a program make it quite unreadable.

The for( ; ; ) construct is much easier to use, in my opinion, even
when it is very complicated as in your example.
-- 
--- Karl Tombre @ CRIN (Centre de Recherche en Informatique de Nancy)
UUCP:    ...!vmucnam!crin!tombre  or    ...!inria!crin!tombre
COSAC:   crin/tombre
POST:    Karl Tombre, CRIN, B.P. 239, 54506 VANDOEUVRE CEDEX, France

"Car le plus lourd fardeau, c'est d'exister sans vivre."
                                  (Victor Hugo)

tp@ndm20 (09/16/85)

> 
>> 	for ( ; (((ch=getch()) < '1' || ch > '5') && ch != 'E') ; )
>> 		putchar(BELL);
>> 	addch(ch);
>> 	refresh();
>> 
>
>I suggest a more appropriate construct, the 'while' loop:
>
>	while(( ch =  getch()) != EOF )
>	{
>		if(( ch < '1' || ch > '5') && ch != 'E')
>			putchar( BELL );
>		addch( ch );
>		refresh();
>	} /* end while */
The second loop does not do the same thing as the first. A better construct
is:

 	for (ch=getch() ; ((ch < '1' || ch > '5') && ch != 'E') ; ch=getch())
 		putchar(BELL);
 	addch(ch);
 	refresh();

Terry Poot
Nathan D. Maier Consulting Engineers
(214)739-4741
Usenet: ...!{allegra|ihnp4}!convex!smu!ndm20!tp
CSNET:  ndm20!tp@smu
ARPA:   ndm20!tp%smu@csnet-relay.ARPA

friesen@psivax.UUCP (Stanley Friesen) (09/16/85)

In article <180@chinet.UUCP> rlk@chinet.UUCP (Richard L. Klappal) writes:
>
>The question:
>	Which of the following code segments is more understandable,
>	(readable, structured, etc) given the current attitudes
>	about the presence of goto's in programming?
>
	Well, of the two, neither really great I prefer this one:
>
>versus NO GOTO VERSION
>
>	for ( ; (((ch=getch()) < '1' || ch > '5') && ch != 'E') ; )
>		putchar(BELL);
>	addch(ch);
>	refresh();
>
That is unless the code is really deeply nested and I need to pop ou
to the outer level on error. BTW, a small nit. Since the 'for' has no
initialization or change expressions it is exactly equivalent to a
'while' statement - it would be more readable if you had written it
that way, or as the someone else suggested, pull the getchar out of
the condition and put it in the init and change expressions.
-- 

				Sarima (Stanley Friesen)

UUCP: {ttidca|ihnp4|sdcrdcf|quad1|nrcvax|bellcore|logico}!psivax!friesen
ARPA: ttidca!psivax!friesen@rand-unix.arpa

franka@mmintl.UUCP (Frank Adams) (09/17/85)

In article <180@chinet.UUCP> rlk@chinet.UUCP (Richard L. Klappal) writes:
>The question:
>	Which of the following code segments is more understandable,
>	(readable, structured, etc) given the current attitudes
>	about the presence of goto's in programming?
>
>GOTO VERSION:
>	. . .
>	noecho();		/* turn off echo */
>retry:
>	move(4,10);		/* set cursor position */
>	refresh();		/* update screen */
>	ch = getch();		/* get input character (without echo) */
>	if ((ch < '1' || ch > '5') && ch != 'E')
>	{			/* valid input is 1 thru 5 or 'E' */
>		putchar(BELL);	/* sound bell on invalid input */
>		goto retry;
>	}
>	addch(ch);		/* echo the valid character */
>	refresh();		/* update screen */
>	. . .
>
>versus NO GOTO VERSION
>
>	for ( ; (((ch=getch()) < '1' || ch > '5') && ch != 'E') ; )
>		putchar(BELL);
>	addch(ch);
>	refresh();

Leaving aside the fact that you left out some statements in the second
version, I would not use either of these styles.  I don't like either gotos
or side effects in conditionals.  I would use something like one of the
following:

	noecho();		/* turn off echo */
	for (;;) {
	   move(4,10);		/* set cursor position */
	   refresh();		/* update screen */
	   ch = getch();	/* get input character (without echo) */
	   if ((ch < '1' || ch > '5') && ch != 'E') {
	 			/* valid input is 1 thru 5 or 'E' */
	      putchar(BELL);	/* sound bell on invalid input */
	   }
	   else {
	      break;
	   }
	}
	addch(ch);		/* echo the valid character */
	refresh();		/* update screen */
	. . .

or

	noecho();		/* turn off echo */
	havech = FALSE;
	while (! havech) {
	   move(4,10);		/* set cursor position */
	   refresh();		/* update screen */
	   ch = getch();	/* get input character (without echo) */
	   havech = (ch >= '1' && ch <= '5') || ch == 'E';
	 			/* valid input is 1 thru 5 or 'E' */
	   if (! havech) {
	      putchar(BELL);	/* sound bell on invalid input */
	   }
	}
	addch(ch);		/* echo the valid character */
	refresh();		/* update screen */
	. . .


Frank Adams                           ihpn4!philabs!pwa-b!mmintl!franka
Multimate International    52 Oakland Ave North    E. Hartford, CT 06108

cottrell@nbs-vms.ARPA (COTTRELL, JAMES) (09/18/85)

/*  Doug gwyn's version
> There are several other non-GOTO ways to rewrite the first example:
> 
> 	move( 4, 10 );			/* position cursor */
> 	refresh();
> 
> 	valid = false;
> 	while ( !valid )
> 		switch ( ch = getch() )	/* validate input char */
> 			{
> 		case 'E':
> 		case '1':
> 		case '2':
> 		case '3':
> 		case '4':
> 		case '5':
> 			valid = true;
> 			break;
> 
> 		default:		/* not valid */
> 			putchar( BELL );
> 			}
> 
> 	addch( ch );			/* echo valid input char */
> 	refresh();
> 
> Although this introduces an additional (Boolean) variable, which is
> one of the problems with goto-less programming, the variable has
> very clear meaning and may actually help in understanding the code.

Axually, you don't need the additional boolean. I'm leaving out
the stuff before & after the while because I know we can code sequences.

 	while (1) {
 		switch (ch = getch()) {	/* validate input char */
 		case 'E':
 		case '1':
 		case '2':
 		case '3':
 		case '4':
 		case '5':
 			break;
 		default:		/* not valid */
 			putchar(BELL);
 			continue;
		}
		break;
	}

I will concede that this starting to become a rat's nest tho.
How about this one:

	do {	switch (ch = getch()) {
 		case 'E':
 		case '1':
 		case '2':
 		case '3':
 		case '4':
 		case '5':
			break;
		default:putchar(ch = BELL);
		}
	} while (ch == BELL);

> > I guess what I'm really asking is:
> > 	If you had to modify this program, written by someone else who
> > 	commented it sparsely, which style would you prefer to work on?
> 
> Mine, of course.

You got the right answer here! Everyone likes their own style best.

Oh. And put the braces IN THE RIGHT PLACE!!! Do it K&R style.
Any other way is fighting a losing battle. You can't beat us. Join us.

	jim		cottrell@nbs
*/
------

mike@peregrine.UUCP (Mike Wexler) (09/18/85)

> 
> The question:
> 	Which of the following code segments is more understandable,
> ...
> versus NO GOTO VERSION
> 
> 	for ( ; (((ch=getch()) < '1' || ch > '5') && ch != 'E') ; )
> 		putchar(BELL);
> 	addch(ch);
> 	refresh();
> 
How about this version
/* input characters until either "E" or a number between 1 and 5 is input */
	for (;;) {
		ch=getch();
		if ((ch>='1'&&ch<='5')||ch=='E') break;
		putchar(BELL);
	}
	addch(ch);
	refresh();

	Some people go to great lengths to get efficiency.  In doing
this they sometimes make there code more difficult to read.
	If a piece of code is difficult to read, then simplify it. There
are many ways of doing this other than putting in goto's.  Goto's should
be avoided accept when they simplify code THAT CAN't BE SIMPLIFIED some
other way.
Mike(always a dreamer) Wexler
15530 Rockfield, Building C
Irvine, Ca 92718
(714)855-3923
(trwrb|scgvaxd)!felix!peregrine!mike

-- 
Mike(always a dreamer) Wexler
15530 Rockfield, Building C
Irvine, Ca 92718
(714)855-3923
(trwrb|scgvaxd)!felix!peregrine!mike

peter@graffiti.UUCP (Peter da Silva) (09/23/85)

> versus NO GOTO VERSION
> 
> 	for ( ; (((ch=getch()) < '1' || ch > '5') && ch != 'E') ; )
> 		putchar(BELL);
> 	addch(ch);
> 	refresh();
> 
> My background is predominately from a FORTRAN-BASIC-PL/I
> environment, so I tend to think of the FOR (.;.;.){} in terms
> of DO .. CONTINUE, iterative DO .. END or DO WHILE .. END constructs,
> where this kind of an assignment in the conditional is verboten.

Obviously. A 'C' programmer would have written:

	while( ((ch=getch()) < '1' || ch > '5') && ch != 'E')
		beep();
	addch(ch);
	refresh();

It's considered bad form to have a for loop with more than 1 empty feild.
Now that you're using a construct that doesn't exist in FORTRAN or BASIC
do you feel better?

tp@ndm20 (09/24/85)

>How about this version
>/* input characters until either "E" or a number between 1 and 5 is input */
>	for (;;) {
>		ch=getch();
>		if ((ch>='1'&&ch<='5')||ch=='E') break;
>		putchar(BELL);
>	}
>	addch(ch);
>	refresh();

Some of us feel the same about breaks as we do about gotos. I won't use it, or
continue either, and I won't let anyone in my shop use it. It is just a goto
with an implicit label, and destroys the ability to determine program flow from
the FORM of the source even more drastically, as there is no label to catch 
your eye and alert you to the presence of the d*mned thing. The only way I will
use a break is in a set of macros implementing a middle exit loop construct.
We use LOOP...LOOPUNTIL...ENDLOOP. I know, adding features to a language with
#define has problems, but at least that code is readable once you know what the
constructs do, which is quite apparent. C should have that structure built in.
Since it doesn't, we add it. The keywords are at the same indentation level, of
course.

Thanks,
Terry Poot
Nathan D. Maier Consulting Engineers
(214)739-4741
Usenet: ...!{allegra|ihnp4}!convex!smu!ndm20!tp
CSNET:  ndm20!tp@smu
ARPA:   ndm20!tp%smu@csnet-relay.ARPA

(-: Someone challenged me to produce flames. This should do it :-)

kenny@uiucdcsb.CS.UIUC.EDU (09/25/85)

/* Written  6:53 am  Sep 23, 1985 by peter@graffiti.UUCP in uiucdcsb:net.lang.c */
>> 
>> 	for ( ; (((ch=getch()) < '1' || ch > '5') && ch != 'E') ; )
>> 		putchar(BELL);
>> 	addch(ch);
>> 	refresh();
>
>Obviously. A 'C' programmer would have written:
>
>	while( ((ch=getch()) < '1' || ch > '5') && ch != 'E')
>		beep();
>	addch(ch);
>	refresh();
>
>It's considered bad form to have a for loop with more than 1 empty feild.
>Now that you're using a construct that doesn't exist in FORTRAN or BASIC
>do you feel better?
/* End of text from uiucdcsb:net.lang.c */

For Heaven's sake, the big problem with this code is that the conditional
is quite unreadable in in-line code like this.  I'd prefer

	while (illegal (ch = getch()))
		beep ();
	addch (ch);
	refresh ();
	   ...
int illegal (ch)
 char ch;
 {
	return ((ch < 1 || ch > 5)
	     && ch != 'E');
 }

which at least separates the test from the action.  I realize that it also
moves the test further away physically in the code, but I think the price
is worthwhile.  

k**2          kenny@uiuc.ARPA       ...!pur-ee!uiucdcs!kenny

Disclaimer: Opinions expressed herein may not be even MINE, much less my
employer's.

dimitrov@csd2.UUCP (Isaac Dimitrovsky) (09/27/85)

[]
Someone asked for a readable version of the following code:
>[example reformatted a bit]
>
>	noecho();
>retry:
>	move(4,10);
>	refresh();
>	ch = getch();
>	if ((ch < '1' || ch > '5') && ch != 'E') {
>		putchar(BELL);
>		goto retry;
>	}
>	addch(ch);
>	refresh();

OK, I've now read enough different responses on this to want to finish this
topic off once and for all. This code is an example of the general pattern:

	repeat
		do stuff;
		if termination condition has not been satisfied then
			give error indication;
	until the termination condition becomes true;

If the stuff to be done is very simple, this can be tersely
expressed in C as follows:

	while (combine doing stuff and check that termination condition is
			not true into one messy expression)
		give error indication;

or, more readably, as:

	for (do stuff; termination condition is not true; do stuff)
		give error indication;

For example, if we assume that the retry label in the code above is
not jumped to from anywhere else, then we could move it down two lines
to just before ch=getch() (the calls to move and refresh should not be
needed since echoing is turned off with noecho). This assumption is made
in all the goto-less versions of this code segment that I've seen.
Then the "stuff" to be done just reduces to the assignment ch=getch(),
and so the code segment can be written tersely as:

	noecho();
	move(4,10);
	refresh();
	while (((ch=getch())<'1' || ch>'5') && ch!='E')
		putchar(BELL);
	addch(ch);
	refresh();

or, the while loop could be rewritten:

	for (ch=getch(); ((ch<'1' || ch>'5') && ch!='E'); ch=getch())
		putchar(BELL);

If the stuff to be done is more complicated, these two forms quickly
become unreadable. In this case, you can either combine the stuff to
be done as a separate function, and then use the above forms again,
or use a do-while loop:

	do {
		do stuff;
		if (termination condition is not true)
			give error indication;
	} while (termination condition is not true);

or, if the check for the termination condition is very expensive:

	do {
		do stuff;
		if (termination condition is not true)
			give error indication;
		else
			break;
	} while (TRUE);

For example, suppose the calls to move and refresh above *are* necessary
before each call to getch. The code above could then be rewritten:

mygetch() {
	move(4,10);
	refresh();
	return getch();
}

	noecho();
	while (((ch=mygetch())<'1' || ch>'5') && ch!='E')
		putchar(BELL);
	addch(ch);
	refresh();

or using a do-while loop:

#define invalid(ch) (((ch)<'1' || (ch)>'5') && (ch)!='E')

	noecho();
	do {
		move(4,10);
		refresh();
		ch=getch();
		if (invalid(ch))
			putchar(BELL);
	} while (invalid(ch));
	addch(ch);
	refresh();

or rewrite the do-while loop as:

	do {
		move(4,10);
		refresh();
		ch=getch();
		if (invalid(ch))
			putchar(BELL);
		else
			break;
	} while (TRUE);

Isaac Dimitrovsky
allegra!cmcl2!csd2!dimitrov   (l in cmcl2 is letter l not number 1)
251 Mercer Street, New York NY 10012     (212) 674-8652

... Hernandez steps in to face ... Orl ... HERchiiiser ... and it's a liiine
driive, deeeeep to the gap in left center ...	- Bob Murphy, Voice of the Mets

rcd@opus.UUCP (Dick Dunn) (10/01/85)

> Some of us feel the same about breaks as we do about gotos. I won't use it, or
> continue either, and I won't let anyone in my shop use it. It is just a goto
> with an implicit label,...

...as are if, while, and for.  So I won't let anyone in my shop use any of
these--but I don't make the rules in my shop for anyone but myself, and I
long ago stopped listening to my own babblings...

Geez, am I trying to one-up a ":-)" or what?  Of course these other
structures are goto's with implicit labels--taking the label out of the
programmer's hands is a way to get some control over the flow structure.

> ...The only way I will
> use a break is in a set of macros implementing a middle exit loop construct.

Well, now we know that this cat's either got some impressive coding
techniques or doesn't use switch statements.  There are only two uses of
break, namely to exit the middle of a loop or to get out of (and usually
terminate) a branch of a switch.  If switch is used without break, I'd like
to see how the coding works.  Other than that, using break only to get out
of the middle of a loop is fairly unspectacular(!).

> We use LOOP...LOOPUNTIL...ENDLOOP. I know, adding features to a language with
> #define has problems, but at least that code is readable once you know what the
> constructs do, which is quite apparent.

OK, great,
	#define	LOOP	for (;;){
	#define	LOOPUNTIL(x)	if (x) break;
	#define	ENDLOOP	}
or some such.  That was a 20-second exercise in turning three items that
any C programmer will understand into three items that he'll have to guess
about or look up somewhere.  If it is not a tautology that "code is (in
some iffy sense) readable once you know what the constructs do", it is at
least fairly certain that "code is not readable until you know what the
constructs do"...and that diddling the language doesn't decrease the number
of constructs.

> C should have that structure built in.
> Since it doesn't, we add it...

Few people who study programming languages seem to understand the idea of
"conceptual consistency" (or integrity).  The pieces of a language have to
hang together.  You can't just twiddle one tiny part of the language to get
rid of your pet peeve and say, "See, I fixed that."  Any change has to fit
in with the overall level of abstraction, spirit, notation, idioms, etc.,
of the language.  Consider, in the given example, that ENDLOOP is a totally
idiosyncratic way of delimiting a statement; all other forms use {}.

Conceptual consistency is what makes languages like C, Pascal, and Icon, as
different as they are, good languages.  It is the reason that they can be
described by small, readable books.  It is the reason a programmer can hold
most of the necessary information about the language in his head rather
than wearing out a manual as he works.  The lack of consistency in
languages like PL/I (to choose an extreme case) leads to big manuals, huge
compilers, and extreme job security for the language's weenies.

It is moderately difficult for a small group of very skilled people, working
together in one place, sharing thoughts and ideas, to produce a good,
consistent language which admits effective implementation and use.  The
idea that a few hacked macros and ad-hoc (??ad-hack??) rules can
noticeably improve the esthetics of a good language is somewhat akin to
the idea that initials carved with a dull knife can improve the esthetics
of a tree:  It's pretty unlikely, but it won't stop people from trying.

> (-: Someone challenged me to produce flames. This should do it :-)

Only if we take you seriously--but it's entertaining in any case.  I guess
I did get in a small flame there at the end.
-- 
Dick Dunn	{hao,ucbvax,allegra}!nbires!rcd		(303)444-5710 x3086
   ...If you plant ice, you're gonna harvest wind.

oleg@birtch.UUCP (Oleg Kiselev x258) (10/02/85)

 Terry Poot writes in <3400010@ndm20>
> Some of us feel the same about breaks as we do about gotos. I won't use it, or
> continue either, and I won't let anyone in my shop use it. It is just a goto
> with an implicit label, and destroys the ability to determine program flow from
> the FORM of the source even more drastically, as there is no label to catch 
> your eye and alert you to the presence of the d*mned thing. The only way I will
> use a break is in a set of macros implementing a middle exit loop construct.
> We use LOOP...LOOPUNTIL...ENDLOOP. I know, adding features to a language with
> #define has problems, but at least that code is readable once you know what the
> constructs do, which is quite apparent. C should have that structure built in.
> Since it doesn't, we add it. The keywords are at the same indentation level, of
> course.
WHY??? What do you need LOOP...etc for when you have "do...while(...);" and 
"while (...) ...." ???? 
And about "break" : HOW do YOU get around "switch" statements ? or do you use
"if... then .... else if .... then ..........."? May be you even #defined that
to be IF, THEN, ELIF, FI like Bourne does? 

Go ahead, do what you feel like! But don't complain when the next time someone
refuses to *even look* at your stuff without cpp-ing it first!

														   Oleg Kiselev

keith@seismo.CSS.GOV (Keith Bostic) (10/02/85)

References: <1556@brl-tgr.ARPA> <139200011@uiucdcsb>

In article <139200011@uiucdcsb>, kenny@uiucdcsb.CS.UIUC.EDU writes:
> For Heaven's sake, the big problem with this code is that the conditional
> is quite unreadable in in-line code like this.  I'd prefer

	while (illegal (ch = getch()))
		beep ();
	addch (ch);
	refresh ();
	   ...
int illegal (ch)
 char ch;
 {
	return ((ch < 1 || ch > 5)
	     && ch != 'E');
 }

> which at least separates the test from the action.  I realize that it also
> moves the test further away physically in the code, but I think the price
> is worthwhile.

It may enhance readability, but it's not worthwhile.  You've just managed
to add a context switch per *character*.  Now, imagine what that's going to
do to a program like spell.  I'm not arguing that everything in the world
should be inline code, but there are still certain real-world limitations
even applications programmers should be aware of.

--keith

guy@sun.uucp (Guy Harris) (10/04/85)

> > For Heaven's sake, the big problem with this code is that the conditional
> > is quite unreadable in in-line code like this.  I'd prefer
> >
> >	while (illegal (ch = getch()))
> >
> > (where "illegal" is defined as a function testing its character
> > argument). 
>
> It may enhance readability, but it's not worthwhile.  You've just managed
> to add a context switch per *character*.  Now, imagine what that's going to
> do to a program like spell.

Yes.  The System V "fgrep" uses a subroutine to do character comparisons.
The 4.2BSD version uses a macro instead.  The 4.2BSD version is
substantially faster.

The function "illegal" in the above example could have been defined as a
macro.  The code calling it would, of course, would have to be rewritten as

	for (;;) {
		ch = getch();
		if (ILLEGAL(ch))
			break;

or somesuch, since the macro uses its argument several times, and thus the
argument can't have side effects.  Mesa, C++, and other languages have the
notion of an "inline function"; when such a function is called, the compiler
doesn't generate a procedure call, but generates code that acts as if the
procedure in question had been called.  In the above example, the code as
written ("while (illegal(ch = getch))") would have compiled into code
equivalent to the macro version.

	Guy Harris

days@glasgow.glasgow.UUCP (Judge Dredd) (10/04/85)

> Some of us feel the same about breaks as we do about gotos. I won't use it, or
> continue either, and I won't let anyone in my shop use it. It is just a goto
	Why use a language if you dislike it ?? (-:

> We use LOOP...LOOPUNTIL...ENDLOOP. I know, adding features to a language with
> #define has problems,but at least that code is readable once you know what the
> constructs do, which is quite apparent. C should have that structure built in.
	Well, continue/break are also perfectly apparent once you know what they
do, and if C had everything that everyone wanted, we'd be left with something
that made York Ada look like tiny-P.

-- 
Stephen Day, Comp Sci Dept, University of Glasgow, Scotland

seismo!mcvax!ukc!glasgow!days		If time were like a treacle bun,
					I would enjoy it so,
					But now it seems it's on the run,
					I'd really better go.

kk@duke.UUCP (Krzysztof Kozminski) (10/06/85)

In article <2848@sun.uucp> Guy Harris writes (quoting somebody else, but in 
apparent support of this other person's critique of yet somebody else):
>> > For Heaven's sake, the big problem with this code is that the conditional
>> > is quite unreadable in in-line code like this.  I'd prefer
>> >	          while (illegal (ch = getch()))
>> > (where "illegal" is defined as a function testing its character argument). 

>> It may enhance readability, but it's not worthwhile.  You've just managed
>> to add a context switch per *character*.  Now, imagine what that's going to
>> do to a program like spell.

>Yes.  The System V "fgrep" uses a subroutine to do character comparisons.  The
>4.2BSD version uses a macro instead. The 4.2BSD version is substantially faster

Uh, aren't you exagerrating a little with all this speed optimization? How many
people use spell or fgrep on input that is hand-typed in real time? Wasn't it
*obvious* from the original article that the code was meant to deal with a live
human on the input end and *not* to read from a file?  Just to remind, this was
something to the effect of:

            while (illegal (ch = getch()))
		make_noise_and_protest_loudly(); /* I.e., putchar(BELL) */

I wish I could type fast enough to see any performance degradation from the
function call overhead in this situation :-)

If I wanted to do a *really fast* I/O with checking if input characters are
legal or not, and unless the set of illegal characters had nice properties
(like <32 or having some particular bit set or something) I'd trade off space
for speed, and do:

#define fast_access char /* or int, depending on the addressability and
		            comaparison speed considerations */
#if EOF != -1
something to call the attention of whoever is going to port this to elsewhere
(portability problems with the "257" below could be resolved analogously).
#endif
fast_access illegal[257];    /* Initialized to evaluate TRUE for illegal and
				FALSE for legal characters. */
#define ILLEGAL(a)	( illegal [1+(a)] )	/* Lots of comments why this */

	while ( ILLEGAL(ch=getchar()) )
	   { complain_in_some_way }

Fast, readable, no problems with side effects in macro substitution, either.
And you can change the illegal set dynamically.

Now if I *really* cared about speed ...  I'd rewrite getchar, too, to avoid the
addition.  Or experiment with:

#define ILLEGAL(a) ((illegal+1)[a])	/* Lots of comments what is this
					   expected to be */

which should avoid run-time addition.  Or I'd write this in assembler.  Or
perhaps design a custom processor :-)

gww@aphasia.UUCP (George Williams) (10/06/85)

> idea that a few hacked macros and ad-hoc (??ad-hack??) rules can

Actually the proper phrase is oddhack which stands for
Organization for Determining Definitive Heuristics Against Catastrophic Kludges.
This was a group at Caltech in about 80.

cjl@iuvax.UUCP (10/14/85)

>> Some of us feel the same about breaks as we do about gotos. I won't use it, 
>> continue either, and I won't let anyone in my shop use it. It is just a goto
>	Why use a language if you dislike it ?? (-:

  "Using an imperfect language" is quite common in the software industry.
  It is for this reason, Ada is born.  But five or ten years later when 
  Ada becomes popular, it is for the same reason we shall find some bugs in
  that language. We can only use a language you like in the university.
  In the real world, we have to use languages we don't like. 

>> We use LOOP...LOOPUNTIL...ENDLOOP. I know, adding features to a language with
>> #define has problems,but at least that code is readable once you know what 
>> constructs do, which is quite apparent. C should have that structure built in.
>	Well, continue/break are also perfectly apparent once you know what they
>do, and if C had everything that everyone wanted, we'd be left with something
>that made York Ada look like tiny-P.

  What we argue here is the style of programs that is easier to reason
  and understood for common people. If we only write a program for ourselves
  then everything is apparent because we know what it does. 
  It is not recommended to change the language tools arbitrarily. But if
  we follow a common agreement , we can improve the coding style of "C"
  without creating a strange appearance.  Any programmer should have
  no difficulty to read LOOP...EXIT...END etc.

C.J.Lo
Dept. of CIS, IUPUI
ARPA : cjl@Indiana@CSNet-Relay
UUCP : ...!iuvax!cjl

henry@utzoo.UUCP (Henry Spencer) (10/18/85)

>   ...  Any programmer should have
>   no difficulty to read LOOP...EXIT...END etc.

Provided it's what he's used to.  If he's a C programmer, it's not.
If he's competent, it won't make a lot of difference, but it will be one
more unnecessary annoyance when he's trying to understand your code.
Is your objective reforming the world, or making your code more readable?
Note that the two are incompatible in the short term, and possibly in the
long term if your reform attempt fails (which I suspect it will).
-- 
				Henry Spencer @ U of Toronto Zoology
				{allegra,ihnp4,linus,decvax}!utzoo!henry

preece@ccvaxa.UUCP (10/24/85)

> LOOP-EXIT-END constructs is aimed to warn explicitly the existence of
> multi-exit points in a loop in modern languages. They are harder to
> understand and reason. Enforcing the use of LOOP-EXIT-END to replace
> the break statement means the simple, natural meaning of for,while and
> do loops is recovered. Therefore the program is more readable because
> loops of different complexity are exposed as they should be.  /*
> Written  3:27 pm  Oct 21, 1985 by cjl@iuvax in ccvaxa:net.lang.c */
----------
I read that (admittedly with some difficulty and room for error) as
saying "Complicated loops ought to look complicated."  It is not
hard to produce an example of a loop which is much more readable
with breaks than with the use of many levels of nesting to
reflect a sequence of computations followed by tests for loop
terminating conditions.  I'll try:

Here's a nice, but deeply nested loop:

    checkreturn = OK;
    for (i=0; i<100 && checkreturn==OK; i++) {
        checkreturn = function1(x);
	if (checkreturn == OK) {
	    checkreturn = function2(x);
	    if (checkreturn == OK) {
	        checkreturn = function3(x);
		if (checkreturn == OK) {
		    checkreturn = function4(x);
		    if (checkreturn == OK) {
		        checkreturn = function5(x);
			if (checkreturn == OK) {
			    result[i] = function6(x);
			}
		        else {
		            result[i] = 0;
		    	}
		    }
		}
	    }
	}
    }

The idea is that the functions are applied in sequence and that
an error can abort computation at any point in the sequence,
stopping execution of the loop.

Now consider the same loop coded with the break statement:

    for (i=0; i<100; i++) {
        checkreturn = function1(x);
	if (checkreturn == BAD) break;

	checkreturn = function2(x);
	if (checkreturn == BAD) break

	checkreturn = function3(x);
	if (checkreturn == BAD) break;

	checkreturn = function4(x);
	if (checkreturn == BAD) break;

	checkreturn = function5(x);
	if (checkreturn == BAD)
	    result[i] = 0;
	else
	    result[i] = function6(x);
    }

Now I'll agree that the nested form better models function with form,
and I'm a great admirer of Louis Sullivan, but in this case I think
the break form is more READABLE to a HUMAN observer.  I think it
better shows that a SEQUENCE of tests is being applied and I think
that the use of the break statement makes it MUCH clearer that
failure of any function leads to termination of the loop, especially
in a real-world example where the nested code may cover several
pages or many screens.

I confess, though (and the faint-hearted might want to skip this
example), I'd probably prefer to see it like this:

    for (i=0; i<100; i++) {
        if ((checkreturn = function1(x)) == BAD) break;

	if ((checkreturn = function2(x)) == BAD) break;

	if ((checkreturn = function3(x)) == BAD) break;

	if ((checkreturn = function4(x)) == BAD) break;

	if ((checkreturn = function5(x)) == BAD) 
	    result[i] = 0;
	else
	    result[i] = function6(x);
    }

FOR ME, vertical whitespace is the enemy of comprehension.  I want
to see a logical unit presented as densely as possible, so long as
things are done in a comprehensibly systematic and orderly way.
In my last example all the constructions are parallel; once you
recognize the pattern, the whole is clearly exposed as a sequence.

The nested example hides the semantic structure in syntactic
structure and is a real pain in the ass when the work done at each
level is even moderately complicated.  The simple, mechanical
application of structuring rules produces a representation that
is perfectly accurate but visually cluttered.  That clutter makes
the representation much harder to work with, especially in a
small-window environment.

-- 
scott preece
gould/csd - urbana
ihnp4!uiucdcs!ccvaxa!preece

BLARSON%ECLD@usc-ecl.ARPA (Bob Larson) (10/26/85)

Your examples are not equivelent, you forgot the "else result[i]=0;"
clause for every if on the nested example.  Besides, this example 
could be put as:

   for(i=0; i < 100 && checkreturn == GOOD; i++){
      if( (checkreturn = function1(x)) == GOOD &&
          (checkreturn = function2(x)) == GOOD &&
          (checkreturn = function3(x)) == GOOD &&
          (checkreturn = function4(x)) == GOOD &&
          (checkreturn = function5(x)) == GOOD )
        result[i]=function6(x);
      else result[i]=0;
   }

The if statement can be replaced by a ?: trianary operator.

Bob Larson 
Arpa: BLarson@usc-ecl.arpa
Uucp: ihnp4!sdcrdcf!oberon!blarson
-------

gwyn@brl-tgr.ARPA (Doug Gwyn <gwyn>) (10/27/85)

If this code needs to grow much more, it might be better to
make it table-driven:

	i = 0;
	do	{
		static int (*funcs[])() = { func1, ..., 0 };
		int (*fp)() = funcs;

		do
			ret[i] = (*fp)( args );
		while ( ret[i] == GOOD && *++fp );
		}
	while ( ret[i] == GOOD && ++i < MAX_RETS );

This example isn't precisely equivalent to the original, but
it accomplishes the same general goal.

mikes@3comvax.UUCP (Mike Shannon) (10/29/85)

Sorry Scott, I don't buy it.  Your example isn't good enough:
Scott's example:
>     checkreturn = OK;
>     for (i=0; i<100 && checkreturn==OK; i++) {
>         checkreturn = function1(x);
> 	if (checkreturn == OK) {
> 	    checkreturn = function2(x);
> 	    if (checkreturn == OK) {
> 	        checkreturn = function3(x);
> 		if (checkreturn == OK) {
> 		    checkreturn = function4(x);
> 		    if (checkreturn == OK) {
> 		        checkreturn = function5(x);
> 			if (checkreturn == OK) {
> 			    result[i] = function6(x);
> 			}
> 		        else {
> 		            result[i] = 0;
> 		    	}
> 		    }
> 		}
> 	    }
> 	}
>     }
> 
-------
Scott's example done the RIGHT (i.e. my :-))  way:
	for(i = 0; i < 100; i++) {
		if(f1(x) == OK &&
		   f2(x) == OK &&
		   f3(x) == OK &&
		   f4(x) == OK &&
		   f5(x) == OK	  ) {
			result[i] = 0;
		}
		else {
			result[i] = function6(x);
		}
	}
--------
	In general, I don't like humongous if statements like the above, but
this one is pretty easy to read.
	I didn't do anything with checkreturn, because Scott's code didn't
use it for anything, either.
	In the more usual case where you need to take different error actions
depending on several successive function calls, I venture to state that
It Should Be Done This Way:
	if(f1() == ERROR) {
		take some sort of error action.
	}
	else if(f2() == ERROR) {
		fix up any side effects of f1,side effects are bad practice too
		take some other sort of error action
	}
	else if(f3() == ERROR) {
		fix up side effects of f1 and f2
		take another error action
	}
	else {
		collect two hundred dollars, you may pass GO
	}
-- 
			Michael Shannon {ihnp4,hplabs}!oliveb!3comvax!mikes

preece@ccvaxa.UUCP (10/29/85)

> Your examples are not equivelent, you forgot the "else result[i]=0;"
> clause for every if on the nested example.  /* Written  6:52 pm  Oct
> 26, 1985 by BLARSON%ECLD@usc-ecl.ARPA in ccvaxa:net.lang.c */
----------
No, there aren't supposed to be else clauses -- the "result[i]=0"
is supposed to happen ONLY if all tests but the last are passed.
That is, the function1 - function5 tests are checking whether it
is valid to apply the actual test and the failure of any of them
is supposed to suppress assignment and terminate the loop.

The suggestion that the tests could be written as a single
expression by joining them with ANDs is fine for the simplified
example, but not applicable if there were additional code involved
in setting up before each test

Several other comments were received.  A couple of people suggested
using an array of functions and looping over that array.  That also
wouldn't work if there were additional code preceding each call,
but is reasonable for the simplified example.  Care would be required
in making it clear what was being done and how to find out which
functions were being called in what order, but this approach has a
nice generality (the list of functions could be built from a
description, automatically).

Prof. Lo also wrote to clarify the posting about LOOP-EXIT-END to
which I originally responded.  The intent was to not use a for
loop where it is possible to exit without completing the iteration.
That's reasonable, though I think it can be argued that the use of
the "for" construct makes it clearer that the NORMAL behavior is
intended to be a loop over those hundred values and the annotation
should make clear under what circumstances an ABNORMAL exit is
made before the end.  It is important to readability that the
language used make clear not only WHAT happens but also WHY.

-- 
scott preece
gould/csd - urbana
ihnp4!uiucdcs!ccvaxa!preece

preece@ccvaxa.UUCP (10/30/85)

> /* Written  7:44 pm  Oct 28, 1985 by mikes@3comvax.UUCP in
> ccvaxa:net.lang.c */
> Scott's example done the RIGHT (i.e. my :-))  way:
> 	for(i = 0; i < 100; i++) {
> 		if(f1(x) == OK &&
> 		   f2(x) == OK &&
> ...
----------
Sigh.  or "Posting Examples Considered Harmful."  The example wasn't
meant to be complete or realistic, it was intended to be a skeletal
example of the KIND of coding that happens using nesting as opposed
to the KIND of coding that happens using break.  The short-circuited
evaluation of the big if works fine if you don't do anything in between
the tests, as my skeletal example didn't.  But if there is some
tissue between the bones you either have to write horrible expressions
that are MUCH harder to read or you have to write the logical structure
as separate tests so that you can have blocks of code between the
tests.
----------
> I didn't do anything with checkreturn, because Scott's code didn't use
> it for anything, either.
----------
You, like some other respondents, failed to notice that my loop
terminates on error; yours just assigns zero to cells on which
the error condition was noticed.  Checkreturn was used (in the nested
example) for that purpose.
----------
> In the more usual case where you need to take different error actions
> depending on several successive function calls, I venture to state that
> It Should Be Done This Way: ...
----------
Your framework is perfectly appropriate for the kind of situation it
addresses, which is different from the kind of situation I was
describing.

I liked your pointing out that the error clauses had to back out any
side effects of all previously applied functions, which is an important
reminder.  On the other hand, the blanket statement that side effects
are bad practice is a little hard to support.  Define "side effects."
Do you mean that no function call should alter the global state of
the computation except by the value(s) it returns?  That's a pretty
tall order.  Many function calls are done specifically for their
side effects (I/O, for instance).  In an error checking sequence
you would like to appear (to higher levels of the program) to be
performing an atomic action, but I think it's unrealistic to demand
more than that there be some way of resetting the state when an
error is detected.

-- 
scott preece
gould/csd - urbana
ihnp4!uiucdcs!ccvaxa!preece

alexis@reed.UUCP (Alexis Dimitriadis) (10/31/85)

In article <538@ttrdc.UUCP> levy@ttrdc.UUCP (Daniel R. Levy) writes:
>[...] The && operator
>doesn't GUARANTEE the chronological order of evaluation is going to be left
>to right, if you have a screwball compiler (though it most probably will be).

Dear Dan:
  You have just inserted your foot in your mouth, in full view of the users
at over 2000 sites around the world.  Observe:

	"Expressions connected by && or || are evaluated left to right, and
	evaluation stops as soon as the truth or falsehood of the result is
	known.  These properties are critical to writing programs that work."
	[K&R, p.38]

  Why, oh why, do we have to endlessly argue fundamental language features?
Please check your facts before you enlighten the net with your wisdom, folks!

Alexis Dimitriadis
-- 
_______________________________________________
  As soon as I get a full time job, the opinions expressed above
will attach themselves to my employer, who will never be rid of
them again.
				alexis @ reed
    {decvax,ihnp4,ucbcad,uw-beaver}!tektronix!reed.UUCP

preece@ccvaxa.UUCP (10/31/85)

> /* Written  8:54 pm  Oct 29, 1985 by levy@ttrdc.UUCP in
> ccvaxa:net.lang.c */ 
> The && operator doesn't GUARANTEE the chronological order of evaluation
> is going to be left to right, if you have a screwball compiler (though
> it most probably will be).
----------
"Expressions connected by && or || are evaluated left to right,
and it is guaranteed that evaluation will stop as soon as the
truth or falsehood is known." [K&R, p19]

-- 
scott preece
gould/csd - urbana
ihnp4!uiucdcs!ccvaxa!preece

levy@ttrdc.UUCP (Daniel R. Levy) (11/01/85)

In article <2081@reed.UUCP>, alexis@reed.UUCP (Alexis Dimitriadis) 
	(as about a jillion others) writes:
>In article <538@ttrdc.UUCP> levy@ttrdc.UUCP (Daniel R. Levy) writes:
>>[...] The && operator
>>doesn't GUARANTEE the chronological order of evaluation is going to be left
>>to right, if you have a screwball compiler (though it most probably will be).
>Dear Dan:
>  You have just inserted your foot in your mouth, in full view of the users
>at over 2000 sites around the world.  Observe:

** REPLACE THIS LINE WITH MY STUPIDITY **

I have already recanted this article, which I wrote in a spasm of C paranoia.
I have also issued three cancel messages for it so it will come to a premature
demise at over 2000 sites around the world.

Why, why, WHY will people roundly flame a guy/gal who has made a collossal
gaffe like mine, yet not issue a peep when a plea goes out far and wide for
some kind of information???  Someone wrote a piece of doggerel about this some
time ago:  "I put a question to the net; I haven't found the answer yet..."
(describes being flamed, etc.).  I recently posted a question asking about
an undocumented flag in VMS C "open" statements for reading the entire rec-
ords of Fortran-format files, and save for one unnamed kindhearted soul from
DEC who mailed me an answer, you could have heard a pin drop (and the sub-
ject had been roundly discussed just a couple of months ago, but it was ir-
relevant to me at that time so I let it go...karmic retribution I guess).
-- 
 -------------------------------    Disclaimer:  The views contained herein are
|       dan levy | yvel nad      |  my own and are not at all those of my em-
|         an engihacker @        |  ployer or the administrator of any computer
| at&t computer systems division |  upon which I may hack.
|        skokie, illinois        |
 --------------------------------   Path: ..!ihnp4!ttrdc!levy

cdl@mplvax.UUCP (Carl Lowenstein) (11/01/85)

In article <538@ttrdc.UUCP> levy@ttrdc.UUCP (Daniel R. Levy) writes:
>In article <259@3comvax.UUCP>, mikes@3comvax.UUCP (Mike Shannon) writes:
>>Sorry Scott, I don't buy it.  Your example isn't good enough:
>functions may be interdependent, share variables, etc, and the order of eval-
>uation may be important, or maybe the first checks are very quick and the
>latter ones are slow, making the order important to performance optimization
>if it is likely that one of the quick checks would fail.  The && operator
>doesn't GUARANTEE the chronological order of evaluation is going to be left
>to right, if you have a screwball compiler (though it most probably will be).

Kernighan & Ritchie, The C Programming Language, p. 190:

"Unlike &, && guarantees left-to-right evaluation; moreover the second
operand is not evaluated if the first operand is 0."

-- 
	carl lowenstein		marine physical lab	u.c. san diego
	{ihnp4|decvax|akgua|dcdwest|ucbvax}	!sdcsvax!mplvax!cdl

jsdy@hadron.UUCP (Joseph S. D. Yao) (11/03/85)

In article <538@ttrdc.UUCP> levy@ttrdc.UUCP (Daniel R. Levy) writes:
>                                                     ...  The && operator
>doesn't GUARANTEE the chronological order of evaluation is going to be left
>to right, if you have a screwball compiler (though it most probably will be).

Only screwball compilers.  In C, the evaluation of '||' and '&&' is
defined to be L-R.  However, the evaluation of '|' and '&' is not.
-- 

	Joe Yao		hadron!jsdy@seismo.{CSS.GOV,ARPA,UUCP}