[net.lang.c] A good use of a bad feature

jim@umcp-cs.UUCP (Jim Williams) (05/03/86)

While discussing various features and miss-features of C
with my friend Charley (mangoe) Wingate recently, we both
agreed that the fall through case statement is among the least
defensible of C's features.  I submit the program below as the
best use I have ever found for this feature.

--------------- CUT HERE ------------
/*
 * xmas.c - a program to print The Twelve Days of Christmas
 * using the C fall thru case statement.
 * If this wasn't my idea, I appologize to whomever I
 * got the idea from, but I wrote the program 5 years
 * ago and I don't remember now. 
 *
 * Jim Williams, jim@maryland, 2 May 1986
 */

/* 
 * If you have an ANSI compatible terminal then
 * #define ANSITTY.  It makes the five Golden rings 
 * especially tacky.
 */

#define ANSITTY

#include <stdio.h>

char *day_name[] = {
	"",
	"first",
	"second",
	"third",
	"fourth",
	"fifth",
	"sixth",
	"seventh",
	"eighth",
	"ninth",
	"tenth",
	"eleventh",
	"twelfth"
	};


main()
{
	int day;

	printf("The Twelve Days of Christmas.\n\n");

	for (day=1; day<=12; day++) {
		printf("On the %s day of Christmas, my true love gave to me\n",
			day_name[day]);

		switch (day) {
		case 12:
			printf("\tTwelve drummers drumming,\n");
		case 11:
			printf("\tEleven lords a leaping,\n");
		case 10:
			printf("\tTen ladies dancing,\n");
		case 9:
			printf("\tNine pipers piping,\n");
		case 8:
			printf("\tEight maids a milking,\n");
		case 7:
			printf("\tSeven swans a swimming,\n");
		case 6:
			printf("\tSix geese a laying,\n");
		case 5:
#ifdef ANSITTY
			printf("\tFive [1;5;7mGolden[0m rings,\n");
#else
			printf("\tFive Golden rings,\n");
#endif
		case 4:
			printf("\tFour calling birds,\n");
		case 3:
			printf("\tThree French hens,\n");
		case 2:
			printf("\tTwo turtle doves, and\n");
		case 1:
			printf("\tA partridge in a pear tree.\n\n");
		}
	}
}
-- 
Jim \/\/illiams
jim@mimsy.umd.edu
umcp-cs!jim.UUCP

barmar@mit-eddie.MIT.EDU (Barry Margolin) (05/04/86)

In article <1298@umcp-cs.UUCP> jim@umcp-cs.UUCP (Jim Williams) writes:
>... the fall through case statement is among the least
>defensible of C's features.

The only defense I can think of it is that it is more easily transformed
into the non-fall-thru version than the other way around.  All you have
to do is add lots of "break" statements.  If you want the effect of the
fall-thru style in a language that only executes one case, you must
either duplicate code or use lots of gotos in many cases.  Note that
this would not be so for your "12 Days of Christmas" program, because it
could easily be implemented as a for loop that invoked each appropriate
case in turn.  But in the cases where the alternatives are not indexed
so nicely it would not work.

However, I agree with you that it is not really a necesary feature.
-- 
    Barry Margolin
    ARPA: barmar@MIT-Multics
    UUCP: ..!genrad!mit-eddie!barmar

root@icst-cmr (UNIX 4.2 BSD) (05/04/86)

	While discussing various features and miss-features of C
	with my friend Charley (mangoe) Wingate recently, we both
	agreed that the fall through case statement is among the least
	defensible of C's features.  
	
	Jim \/\/illiams
	jim@mimsy.umd.edu
	umcp-cs!jim.UUCP
	
You will find much disagreement on that point, I'm afraid. Fall thru is
more flexible. You can always break, but often cases are related so that
a particular case requires a superset of another case. It is to DMR's
credit that he realized this feature. I sure wouldn't have thought of it.

	(Root Boy) Jim Cottrell		<rbj@cmr>
	"One man gathers what another man spills"

kwh@bentley.UUCP (KW Heuer) (05/09/86)

In article <528@brl-smoke.ARPA> root@icst-cmr ((Root Boy) Jim Cottrell) writes:
[concerning the fall-through case feature]
>You will find much disagreement on that point, I'm afraid. Fall thru is
>more flexible. You can always break, but often cases are related so that
>a particular case requires a superset of another case. It is to DMR's
>credit that he realized this feature. I sure wouldn't have thought of it.

An alternate implementation would have been for cases to be distinct by
default, but "continue" at the bottom of a case would cause a fall-thru.
(In this model, "break" would be meaningless inside a switch and "continue"
would be meaningful, instead of the opposite.)  Given that most use of the
switch statement *does* have distinct cases, and the exceptions are often
explicitly labeled /* no break */, I think this would've been better.
(I would *almost* accept a model with automatic break and no keyword for
fall-thru, and suffer the occasional goto.)

One special instance is where the superset is actually an equivalence, i.e.
two or more labels on the same code:
	case ' ':
	case '\t':
	case '\n':
		white();
		break;
This would be ugly with the auto-break model, but the solution is to allow
	case ' ','\t','\n':
		white();
which I think looks neater anyway.

(Btw, I find
	case '0':
	...
	case '9':
		digit();
annoyingly verbose in the current model, and often use "if" rather than
"switch" because of it.)

Karl W. Z. Heuer (ihnp4!bentley!kwh), The Walking Lint

emjej@uokvax.UUCP (05/09/86)

/* Written 10:49 am  May  4, 1986 by root@icst-cmr in net.lang.c */
You will find much disagreement on that point, I'm afraid. Fall thru is
more flexible. You can always break, but often cases are related so that
a particular case requires a superset of another case. It is to DMR's
credit that he realized this feature. I sure wouldn't have thought of it.
/* End of text from net.lang.c */

Ravening C worship should not get in the way of historical accuracy.  The
fall-through of cases in switch statements was inherited from BCPL.
You have its originators to, uh, thank for it.

						James Jones

zap@duvan.UUCP (Svante Lindahl) (05/10/86)

In article <1298@umcp-cs.UUCP> jim@umcp-cs.UUCP writes:
>/*
> * xmas.c - a program to print The Twelve Days of Christmas
> * using the C fall thru case statement.
> * If this wasn't my idea, I appologize to whomever I
> * got the idea from, but I wrote the program 5 years
> * ago and I don't remember now. 
[...]
Sorry this isnt' C, but I can't resist posting this... though
I realize this is probably not a very apropriate newsgroup.
It isn't my work, but I've lost track of who wrote it. Anyway
it is an amusing piece of Algol-68-code. Example of execution
under the TOPS-10 OS included after the code.

BEGIN
  PROC readint = INT: (INT i; read(i); i);
  INT  one=1, two=2, three=3, four=4, five=5, six=6,
       seven=7, eight=8, nine=9, ten=10, eleven=11, twelve=12;
  INT  a=one;
  PRIO ME=5, LOVE=7, MY=7, LORDS=7, LADIES=7,
       PIPERS=7, DRUMMERS=7, MAIDS=7, SWANS=7, GEESE=7,
       GOLD=7, COLLY=7, FRENCH=7, TURTLE=7, PARTRIDGE=6;
  BOOL sent to=TRUE;
  OP THE =	(BOOL a)BOOL:a,
     TWELFTH =	(INT a)BOOL: a=twelve,
     ELEVENTH =	(INT a)BOOL: a=eleven,
     TENTH =	(INT a)BOOL: a=ten,
     NINTH =	(INT a)BOOL: a=nine,
     EIGHTH =	(INT a)BOOL: a=eight,
     SEVENTH =	(INT a)BOOL: a=seven,
     SIXTH =	(INT a)BOOL: a=six,
     FIFTH =	(INT a)BOOL: a=five,
     FOURTH =	(INT a)BOOL: a=four,
     THIRD =	(INT a)BOOL: a=three,
     SECOND =	(INT a)BOOL: a=two,
     FIRST =	(INT a)BOOL: a=one;
  OP ME =	(BOOL a, INT b)VOID: (IF a THEN print(b) FI),
     LOVE =	(BOOL a, b)BOOL: (IF a THEN b ELSE FALSE FI),
     MY =	(BOOL a, b)BOOL: a LOVE b,
     AND =	(INT a)INT: a;
  MODE DATE =	STRUCT(INT day, month);
  DATE christmas := (25, 12);
  OP LORDS =	(INT a, b)INT: a*b,
     LADIES =	(INT a, b)INT: a*b,
     PIPERS =	(INT a, b)INT: a*b,
     DRUMMERS =	(INT a, b)INT: a*b,
     MAIDS =	(INT a, b)INT: a*b,
     SWANS =	(INT a, b)INT: a*b,
     GEESE =	(INT a, b)INT: a*b,
     GOLD =	(INT a, b)INT: a*b,
     COLLY =	(INT a, b)INT: a*b,
     FRENCH =	(INT a, b)INT: a*b,
     TURTLE =	(INT a, b)INT: a*b;
  OP LEAPING =	(INT a)INT: a,
     DANCING =	(INT a)INT: a,
     PIPING =	(INT a)INT: a,
     DRUMMING =	(INT a)INT: a,
     MILKING =	(INT a)INT: a,
     SWIMMING =	(INT a)INT: a,
     LAYING =	(INT a)INT: a,
     RINGS =	(INT a)INT: a,
     BIRDS =	(INT a)INT: a,
     HENS =	(INT a)INT: a,
     DOVES =	(INT a)INT: a;
  OP PARTRIDGE = (INT a, b)INT: a+b;
  INT in a pear tree = 0;

  print("Factorial of ");
  print(day OF christmas := readint);
  print(" is ");
  IF day OF christmas > 12 THEN
    print("too big for this program.");
    stop
  FI;

	# Now we are ready.. #

   THE FIRST day OF christmas MY TRUE LOVE sent to ME
  a PARTRIDGE in a pear tree;

    THE SECOND day OF christmas MY TRUE LOVE sent to ME
  two TURTLE DOVES AND
  a PARTRIDGE in a pear tree;

    THE THIRD day OF christmas MY TRUE LOVE sent to ME
  three FRENCH HENS
  two TURTLE DOVES AND
  a PARTRIDGE in a pear tree;

    THE FOURTH day OF christmas MY TRUE LOVE sent to ME
  four COLLY BIRDS
  three FRENCH HENS
  two TURTLE DOVES AND
  a PARTRIDGE in a pear tree;

    THE FIFTH day OF christmas MY TRUE LOVE sent to ME
  five GOLD RINGS
  four COLLY BIRDS
  three FRENCH HENS
  two TURTLE DOVES AND
  a PARTRIDGE in a pear tree;

    THE SIXTH day OF christmas MY TRUE LOVE sent to ME
  six GEESE LAYING
  five GOLD RINGS
  four COLLY BIRDS
  three FRENCH HENS
  two TURTLE DOVES AND
  a PARTRIDGE in a pear tree;

    THE SEVENTH day OF christmas MY TRUE LOVE sent to ME
  seven SWANS SWIMMING
  six GEESE LAYING
  five GOLD RINGS
  four COLLY BIRDS
  three FRENCH HENS
  two TURTLE DOVES AND
  a PARTRIDGE in a pear tree;

    THE EIGHTH day OF christmas MY TRUE LOVE sent to ME
  eight MAIDS MILKING
  seven SWANS SWIMMING
  six GEESE LAYING
  five GOLD RINGS
  four COLLY BIRDS
  three FRENCH HENS
  two TURTLE DOVES AND
  a PARTRIDGE in a pear tree;

    THE NINTH day OF christmas MY TRUE LOVE sent to ME
  nine DRUMMERS DRUMMING
  eight MAIDS MILKING
  seven SWANS SWIMMING
  six GEESE LAYING
  five GOLD RINGS
  four COLLY BIRDS
  three FRENCH HENS
  two TURTLE DOVES AND
  a PARTRIDGE in a pear tree;

    THE TENTH day OF christmas MY TRUE LOVE sent to ME
  ten PIPERS PIPING
  nine DRUMMERS DRUMMING
  eight MAIDS MILKING
  seven SWANS SWIMMING
  six GEESE LAYING
  five GOLD RINGS
  four COLLY BIRDS
  three FRENCH HENS
  two TURTLE DOVES AND
  a PARTRIDGE in a pear tree;

    THE ELEVENTH day OF christmas MY TRUE LOVE sent to ME
  eleven LADIES DANCING
  ten PIPERS PIPING
  nine DRUMMERS DRUMMING
  eight MAIDS MILKING
  seven SWANS SWIMMING
  six GEESE LAYING
  five GOLD RINGS
  four COLLY BIRDS
  three FRENCH HENS
  two TURTLE DOVES AND
  a PARTRIDGE in a pear tree;

    THE TWELFTH day OF christmas MY TRUE LOVE sent to ME
  twelve LORDS LEAPING
  eleven LADIES DANCING
  ten PIPERS PIPING
  nine DRUMMERS DRUMMING
  eight MAIDS MILKING
  seven SWANS SWIMMING
  six GEESE LAYING
  five GOLD RINGS
  four COLLY BIRDS
  three FRENCH HENS
  two TURTLE DOVES AND
  a PARTRIDGE in a pear tree

END


.execut xmas
A68:	XMAS
ALGOL68C Release 1.271
Unused space 991

Code generated 8r2271

LINK:	Loading
[LNKXCT	A68 execution]
7
Factorial of            +7 is         +5040

Svante Lindahl, NADA, KTH            Numerical Analysis & Computer Science 
UUCP: {seismo,mcvax}!enea!ttds!zap   Royal Institute of Technology, Sweden
ARPA: enea!ttds!zap@seismo.CSS.GOV   EAN: zap@cs.kth.sunet

aglew@ccvaxa.UUCP (05/10/86)

>/* Written 10:49 am  May  4, 1986 by root@icst-cmr in net.lang.c */
>You will find much disagreement on that point, I'm afraid. Fall thru is
>more flexible. You can always break, but often cases are related so that
>a particular case requires a superset of another case. It is to DMR's
>credit that he realized this feature. I sure wouldn't have thought of it.
>/* End of text from net.lang.c */

And what do you do if you have more than one case which is a superset of
another? Eg.

	case A      case B
	      \    /
	       \  /
             case C
                |
                |
                break

Dup code or use gotos. The worst is when people use combinations of gotos
and fall-through:

	case A: 
		...
		goto case_C;
	case B:
		...
		/* fall through */
	case C:
	case_C:
		...
		break;

There should be one way to do things. Special case constructs should only be
used if they crop up frequently. I don't think fall through qualifies.

Andy "Krazy" Glew. Gould CSD-Urbana.    USEnet:  ihnp4!uiucdcs!ccvaxa!aglew
1101 E. University, Urbana, IL 61801    ARPAnet: aglew@gswd-vms

rbj@icst-cmr (Root Boy Jim) (05/13/86)

> root@icst-cmr ((Root Boy) Jim Cottrell) writes:
> [concerning the fall-through case feature]
> >You will find much disagreement on that point, I'm afraid. Fall thru is
> >more flexible. You can always break, but often cases are related so that
> >a particular case requires a superset of another case. It is to DMR's
> >credit that he realized this feature. I sure wouldn't have thought of it.
> 
> An alternate implementation would have been for cases to be distinct by
> default, but "continue" at the bottom of a case would cause a fall-thru.
> (In this model, "break" would be meaningless inside a switch and "continue"
> would be meaningful, instead of the opposite.) 

Interesting. We would give up the ability to contine a loop from within
a switch, but gain the abilty to break from one. This might be a win.

> Given that most use of the
> switch statement *does* have distinct cases, and the exceptions are often
> explicitly labeled /* no break */, I think this would've been better.
> (I would *almost* accept a model with automatic break and no keyword for
> fall-thru, and suffer the occasional goto.)

The point I was trying to make is that given two alternatives, the one
that can simulate the other is more powerful. One could lose this benefit
by being to complex or abstruse, but that is another story. Note that assuming
absence of a feature (break) is easier to generate code for (and get around
from a programming point of view) than assuming presence of same.

> One special instance is where the superset is actually an equivalence, i.e.
> two or more labels on the same code:
> 	case ' ':
> 	case '\t':
> 	case '\n':
> 		white();
> 		break;

Which occurs quite often.

> This would be ugly with the auto-break model, but the solution is to allow
> 	case ' ','\t','\n':
> 		white();
> which I think looks neater anyway.
 
I like it too. Other neat things use this convention. Some bad ones do too.

> (Btw, I find
> 	case '0':
> 	...
> 	case '9':
> 		digit();
> annoyingly verbose in the current model, and often use "if" rather than
> "switch" because of it.)

Or you could use `isdigit'. Or make up a table (say for ascii char syntax
attributes) bitwise encoded, natch!

> Karl W. Z. Heuer (ihnp4!bentley!kwh), The Walking Lint

	(Root Boy) Jim Cottrell		<rbj@cmr>
	"One man gathers what another man spills"