ray@micomvax.UUCP (Ray Dunn) (03/16/88)
[References to various postings giving various proposed incarnations of
switch syntax ommitted]
My 2-penny-worth is that the switch statement is anyway too restrictive with
its integer constant expression values, and that making a special case [pun
not intended] of this was Not Really Worthwhile.
Case statements should just be generalised into a "shorthand" way of
stating if...elseif...elseif...else...
So, (simplified to give jist rather than be definitive):
switch
{ [declaration]
case (expression):
.
[statement]
.
case (expression):
.
[statement]
.
default:
.
[statement]
.
}
Where the expression in each "case (expression)" is a full boolean
expression as in "if (expression)".
There could of course be a (bad) argument to say that if "switch {" appears
as "switch (expression) {" then the previous constant expression meaning of
the switch is used - pretend I didn't suggest that.
Automatic fallthrough should *not* take place, and the common fallthrough
requirement of:
case 1:
case 2:
case 3:
becomes a simple 'or'ed boolean expression.
Compilers can easily generate just as "efficient" code when only constant
expression values are involved (MSC 4.0 seems to generate worse code when you
use a case statement than when you use an if...else if...else... sequence!).
[God! I've just realized that this is aprroximately the way case statements
are defined in that *wonderful* (:-) language dBaseIII - so, I know, I've
just lost any credibility I may have had!!]
Ray Dunn. ..{philabs, mnetor, musocs}!micomvax!ray
franka@mmintl.UUCP (Frank Adams) (03/17/88)
In article <941@micomvax.UUCP> ray@micomvax.UUCP (Ray Dunn) writes: >Case statements should just be generalised into a "shorthand" way of >stating if...elseif...elseif...else... Permit me to register my strong disagreement. if...elseif...elseif...else... should be written: if (...) { ... } else if (...) { ... } else if (...) { ... } else { ... } (positioning the brackets as you see fit. Please let's not start *that* discussion again!) Computer languages don't need two equally attractive ways to say the same thing. -- Frank Adams ihnp4!philabs!pwa-b!mmintl!franka Ashton-Tate 52 Oakland Ave North E. Hartford, CT 06108
karl@haddock.ISC.COM (Karl Heuer) (03/21/88)
In article <941@micomvax.UUCP> ray@micomvax.UUCP (Ray Dunn) writes: >Case statements should just be generalised into a "shorthand" way of >stating if...elseif...elseif...else... I disagree. The else-if chains already provide a perfectly good way of doing that, and are just as concise. There's no need to add a second syntax of equivalent power. In fact, the existing switch statment is more concise in the job for which it was designed: you can write "case VALUE:" instead of "case (long_hairy_expression == VALUE):". If the controlling expression has side effects, your "enhanced" switch would require an extra temporary. Yes, in a sense switch is less powerful than an else-if chain. Let's keep it, for the same reasons that we retain flow constructs less powerful than goto. The existing switch isn't perfect, of course. I do think we should get rid of automatic fallthrough, and allow a list of values (or a list of ranges) for each label. Then the case-blocks would be commutative. >Compilers can easily generate just as "efficient" code when only constant >expression values are involved Oh? Do any compilers currently optimize if-else statements in this way? >(MSC 4.0 seems to generate worse code when you use a case statement than when >you use an if...else if...else... sequence!). A compiler that doesn't optimize the existing switch is unlikely to do anything intelligent with the generalization. Karl W. Z. Heuer (ima!haddock!karl or karl@haddock.isc.com), The Walking Lint
g-rh@cca.CCA.COM (Richard Harter) (03/22/88)
In article <3074@haddock.ISC.COM> karl@haddock.ima.isc.com (Karl Heuer) writes: >In article <941@micomvax.UUCP> ray@micomvax.UUCP (Ray Dunn) writes: > >Yes, in a sense switch is less powerful than an else-if chain. Let's keep it, >for the same reasons that we retain flow constructs less powerful than goto. > Theoretically the switch construct is more powerful than an else-if chain because it selects in one step. Execution is O(1) rather than O(n) where n is the number of branches. It is, for example, considerably more efficient to use a switch with a thousand cases than an else-if chain with a thousand elses. [This is not to be taken as an endorsement of such code. :-)] -- In the fields of Hell where the grass grows high Are the graves of dreams allowed to die. Richard Harter, SMDS Inc.
edw@IUS1.CS.CMU.EDU (Eddie Wyatt) (03/22/88)
>> >>Yes, in a sense switch is less powerful than an else-if chain. Let's keep it, >>for the same reasons that we retain flow constructs less powerful than goto. >> > Theoretically the switch construct is more powerful than an else-if > chain because it selects in one step. Execution is O(1) rather than O(n) > where n is the number of branches. It is, for example, considerably more > efficient to use a switch with a thousand cases than an else-if chain with > a thousand elses. [This is not to be taken as an endorsement of such > code. :-)] I think he meant in terms of expressability. Which is true in a practical sense, false in theorectically. 'switch' and 'else-if' are equivalent in terms of expressability. -- Eddie Wyatt e-mail: edw@ius1.cs.cmu.edu
barmar@think.COM (Barry Margolin) (03/23/88)
In article <1186@PT.CS.CMU.EDU> edw@IUS1.CS.CMU.EDU (Eddie Wyatt) writes: > >>Yes, in a sense switch is less powerful than an else-if chain. Let's keep it, > >>for the same reasons that we retain flow constructs less powerful than goto. > > Theoretically the switch construct is more powerful than an else-if > > chain because it selects in one step. Execution is O(1) rather than O(n) > > where n is the number of branches. It is, for example, considerably more > > efficient to use a switch with a thousand cases than an else-if chain with > > a thousand elses. [This is not to be taken as an endorsement of such > > code. :-)] > I think he meant in terms of expressability. Which is true in a >practical sense, false in theorectically. 'switch' and 'else-if' >are equivalent in terms of expressability. That is obviously untrue. A switch can only check one expression, and can only compare it against constant values. An else-if chain can check for multiple conditions and can compare computed values. You can even have side effects in the conditional expressions, which can affect later conditionals (I don't recommend this practice taken to extreme, but things like 'if ((ch = getchar()) == EOF) ... else if (ch == '\n') ...' aren't too bad). Barry Margolin Thinking Machines Corp. barmar@think.com uunet!think!barmar
edw@IUS1.CS.CMU.EDU (Eddie Wyatt) (03/23/88)
> > I think he meant in terms of expressability. Which is true in a > >practical sense, false in theorectically. 'switch' and 'else-if' > >are equivalent in terms of expressability. > > That is obviously untrue. A switch can only check one expression, and > can only compare it against constant values. An else-if chain can > check for multiple conditions and can compare computed values. You > can even have side effects in the conditional expressions, which can > affect later conditionals (I don't recommend this practice taken to > extreme, but things like 'if ((ch = getchar()) == EOF) ... else if (ch > == '\n') ...' aren't too bad). Outline of the prove that switch could replace an else-if chain. The opposite direction is trivial and is left for the ready to prove :-) Actually one needs to do an induction proof, but this is just an outline. if (cond1) expr1 else if (cond2) expr2 else if (cond3) expr3 .... else if (condN) exprN is equivalently rewritten as: if (cond1) expr1 else { if (cond2) expr2 else { if (cond3) expr3 else { ....... } } } forever pair of if (cond) expr1 else expr2 rewrite the statement as: switch(cond) { case TRUE : expr1; break; case FALSE : expr2; break; } so the else-if chain becomes: switch(cond1) { case TRUE : expr1; break; case FALSE : switch (cond2) { case TRUE : expr2; break; case FALSE : ...... } break; } Remember what I said, in a practical sense, yes one needs else-ifs but in a theorical sense, you gain nothing in terms of the expressable functions. -- Eddie Wyatt e-mail: edw@ius1.cs.cmu.edu
richard@aiva.ed.ac.uk (Richard Tobin) (03/23/88)
In article <25835@cca.CCA.COM> g-rh@CCA.CCA.COM.UUCP (Richard Harter) writes: > Theoretically the switch construct is more powerful than an else-if >chain because it selects in one step. Execution is O(1) rather than O(n) >where n is the number of branches. No, *practically* the switch construct is more powerful because it selects in one step. *Theoretically* switch can compile into an if-then-else chain (and often will, if the case values are widely separated), and an if-then-else chain can be analysed to discover whether it can be compiled into a jump table. The theoretical power of a language is [determined by] the set of algorithms it can implement. Its efficiency in executing these algorithms depends on its implementation, though it's obviously easier to produce efficient implementations of some languages than others. -- Richard Tobin, JANET: R.Tobin@uk.ac.ed AI Applications Institute, ARPA: R.Tobin%uk.ac.ed@nss.cs.ucl.ac.uk Edinburgh University. UUCP: ...!ukc!ed.ac.uk!R.Tobin
g-rh@cca.CCA.COM (Richard Harter) (03/23/88)
In article <18377@think.UUCP> barmar@fafnir.think.com.UUCP (Barry Margolin) writes: >> > Theoretically the switch construct is more powerful than an else-if >> > chain because it selects in one step. Execution is O(1) rather than O(n) >> > where n is the number of branches. It is, for example, considerably more >> > efficient to use a switch with a thousand cases than an else-if chain with >> > a thousand elses. [This is not to be taken as an endorsement of such >> > code. :-)] >> I think he meant in terms of expressability. Which is true in a >>practical sense, false in theorectically. 'switch' and 'else-if' >>are equivalent in terms of expressability. >That is obviously untrue. A switch can only check one expression, and >can only compare it against constant values. An else-if chain can >check for multiple conditions and can compare computed values. You >can even have side effects in the conditional expressions, which can >affect later conditionals (I don't recommend this practice taken to >extreme, but things like 'if ((ch = getchar()) == EOF) ... else if (ch >== '\n') ...' aren't too bad). We're talking apples, oranges, and pears, here. The switch construct is superior to the else-if chain in computational efficiency, in the sense that it can select between n alternatives in one step rather than n steps, when the selection is made on the value of a variable which has a range of length n. An else-if chain can be replaced by successive switches or by a single switch contained within a while loop (in C with no additional if's), e.g. more = 1; kludge = 0; while (more) switch(kludge) { case 0: kludge += (1 + (expr1)); break; case 1: kludge = 3; break; case 2: action1; more = false; break; case 3: kludge += (1 + (expr2)); .... [This code does not have the good programming stamp of approval.] If we ignore questions of computational power and simply consider expressive- ness, else-if chains can obviously replace switches. And, of course, if we program reasonably, we use each where it is appropriate. (Easier said then done.) -- In the fields of Hell where the grass grows high Are the graves of dreams allowed to die. Richard Harter, SMDS Inc.
dwh@twg-ap.UUCP (Dave Hamaker) (03/24/88)
In article <941@micomvax.UUCP>, ray@micomvax.UUCP (Ray Dunn) writes: > Case statements should just be generalised into a "shorthand" way of > stating if...elseif...elseif...else... It seems to me that Ray's proposal doesn't go far enough; it's hard to accept a construct which is just another way of writing an if...elseif...else... (why clutter a language with two ways to say the same thing?). Yet I also feel that case constructs are always artificially so constrained that they violate my sense of language asthetics. In thinking about this a lot, I came up with the notion that the switch expression and case labels can be viewed as textual pieces of a boolean expression whose truth value chooses the specific case label. You get something like: switch [(expression)] { case expression1: ... case expression2: ... default: ... } where the square brackets indicate that "(expression)" is optional. The switch expression is the "left-hand-side" of the boolean expression and expression1/expression2 are the "right-hand-side." As such these are not true expressions; they are really substrings of a constructed expression, although they *may* be complete expressions. They can't be completely arbitrary substrings, though; they must be parentheses-balanced because the switch expression is enclosed in parenthesis, and delimited chars and strings must be complete for a similar reason (how else do you distinguish the closing parenthesis from a parenthesis contained in the constant?). In compiling the construct, you need the ability to determine if expression, expression1 and expression2 are "complete" or not. This is just a matter of whether you can parse them completely. If the switch expression and a case expression are both complete, then you presume a == operator goes between them. Otherwise, you just concatenate them. You choose the first true case, if there is any ambiguity, which amounts to qualifying each case with the negations of all previous ones (the default label is the negation of all the cases). Thus, you get things like: switch (i) { case (<0): ... break; case (0): ... break; case (>0): ... break; } or switch { case (i<0): ... break; case (i==0): ... break; case (i>0): ... break; } or even switch (i==j) { case ([0]): ... break; case ([1]): ... break; case ([2]): ... break; } (which is admittedly pretty weird but only an illustration). If we require complete switch expressions to be evaluated only once, then the current switch syntax is completely contained, and it can be determined if the old conditions are met (and you can compile code which is just as efficient or inefficient as you used to). I wouldn't guarantee any order of case expression evaluation nor that all case expressions would actually be evaluated (lazy evaluation is allowed); otherwise expression evaluation side effects can get in the way of optimization. -Dave Hamaker The Wollongong Group ...!sun!amdahl!twg-ap!dwh
g-rh@cca.CCA.COM (Richard Harter) (03/24/88)
>No, *practically* the switch construct is more powerful because it selects >in one step. *Theoretically* switch can compile into an if-then-else chain >(and often will, if the case values are widely separated), and an >if-then-else chain can be analysed to discover whether it can be compiled >into a jump table. >The theoretical power of a language is [determined by] the set of >algorithms it can implement. Its efficiency in executing these algorithms >depends on its implementation, though it's obviously easier to produce >efficient implementations of some languages than others. This is a useless definition of the power of a language. Almost all languages of any interest can implement a turing machine emulation in which all effective algorithms can be implemented. You can even do it if the only flow-control construct is the 'while' loop. You can pick any measure of strength of constructs that you like -- however the usual measure is space and time efficiency. For example, it can be shown, that if language A provides the if-then-else and the while-loop and language B provides the if-then-else and the goto as their sole flow control constructs, then there are programs of length N and execution time O(N) in language B such that any equivalent program in language A requires either O(N logN) execution time or O(n *exp(N)) space, or some combination thereof. By this criteria the goto is more powerful than the while-loop. The hierarchy of strengths runs while-loop forever-loop-with-multiple-escapes goto procedure-invocation-and-return computed-goto/switch The switch construct is equivalent in power to the computed goto. This measure of power is unambiguous. One can also speak of the expressiveness of a construct, in the sense of what can be easily done in the language. Although this is probably the most useful criteria (and I suspect what you really) meant it is obviously vague and subjective. -- In the fields of Hell where the grass grows high Are the graves of dreams allowed to die. Richard Harter, SMDS Inc.
jrl@anuck.UUCP (j.r.lupien) (03/25/88)
From article <18377@think.UUCP>, by barmar@think.COM (Barry Margolin): > In article <1186@PT.CS.CMU.EDU> edw@IUS1.CS.CMU.EDU (Eddie Wyatt) writes: >> >>Yes, in a sense switch is less powerful than an else-if chain. Let's keep it, >> >>for the same reasons that we retain flow constructs less powerful than goto. >> > Theoretically the switch construct is more powerful than an else-if >> > chain because it selects in one step. Execution is O(1) rather than O(n) >> > where n is the number of branches. Things get even better than this. It is possible to define parameters of switch selection which allow even more optimization. These parameters involve the number of switch values and the range of values. For any given machine architecture, certain ranges of these parameters will perform better using one of: indexed dispatch table, linear search dispatch table, and hashed dispatch table. The compiler can count the number of entries, examine the spread, and select the best implementation for each switch. Naturally "best" means slightly different things to different programmers: some need speed, some need space, some need some arcane relationship between speed and space. Building in a means for the programmer to tell the compiler which feature is more important and by how much would permit the compiler to make even better choices for such cases as switch coding. (for instance, MSC 5.0 has optimization switches to allow selection of speed or space optimazation) John R. Lupien twitch!mvuxa!anuxh!jrl