A case for the BASIC CASE statement
David R. Lane (77) 766 posts |
The CASE statement in BBC BASIC is introduced in manuals as a generalisation of the IF…THEN…ELSE statement to situations where there are more than 2 alternatives. Despite the part between the IF and THEN being a condition that is either true or false (values -1 or 0, respectively, in BBC BASIC), all the examples I have seen of the CASE statement do not involve such conditions. The value of the variable or expression following CASE is usually a value from user input, for example, what key is pressed. See, for example, BBC BASIC Reference Manual, Acorn Computers Limited, 1992, pp 84-86, the StrongHelp manual Basic, by Steve Drain and BBC BASIC V, a dabhand guide, Chapter 4, to be found at www.riscos.com/support/developers/basicv. The last two references include some rather obscure examples, but none involving several conditions with values true or false. Every time I have needed a CASE statement it is as an alternative to nested IF…THEN…ELSE constructs which are neither as clear to understand nor as easy to put together compared to the CASE statement. A simple example of what I might need is for three different actions according as a variable X satisfies either X <= 3, 3 < X <= 7 or X >7. I have thought of a general method of dealing with this where one has n conditions C1, C2,…, Cn which take Boolean values B1, B2,…, Bn for a particular input. So each Bi is -1 (true) or 0 (false). Let |
GavinWraith (26) 1563 posts |
This recapitulates exactly how one uses bitfields as flags for cases in machine code. Clever C compilers transform switch statements so that their cases correspond to consecutive values of the byte/word holding the flags, and then use a multiple of that byte/word as an offset into a jump table. The big difference between C and interpreted languages like BASIC is that the latter have to evaluate the WHEN expressions at run time, whereas C can perform optimizations (perf) because it already knows what their values are. Lua does not have a CASE statement; a matter which is regularly brought up by those relatively new to the language. But you can manufacture one by having tables of functions:
|
Steve Fryatt (216) 2105 posts |
I’m a little unsure what the question is here… BBC BASIC’s Other languages get around this “limitation” of requiring constant values for
However, the fact that BASIC’s
|
Clive Semmens (2335) 3276 posts |
Blimey. That’s quite a generalization. I use CASE a lot. Here’s a typical example: 180 : REPEAT 190 : SYS "Wimp_Poll",mask%, block% TO Event% 200 : CASE Event% OF 210 : WHEN 2:SYS "Wimp_OpenWindow", 0, block% 220 : WHEN 3:SYS "Wimp_CloseWindow", 0, block% 230 : WHEN 6:PROCMouse 240 : WHEN 7:PROCDragBox 250 : WHEN 8:PROCKeyPress 260 : WHEN 9:PROCSelection 270 : WHEN 17,18:PROCMessage 280 : ENDCASE 290 : UNTIL finished% 300 : SYS "Wimp_CloseDown" Well – it’s sort of a value from user input, but not in the sense I think you meant. |
Steve Drain (222) 1620 posts |
This is more effective if there are more checks. The
|
David R. Lane (77) 766 posts |
@Clive Thanks for other replies which I will mull over. |
Clive Semmens (2335) 3276 posts |
Not clear enough for me, certainly, but that’s probably more my fault than yours. |
Rick Murray (539) 13851 posts |
That is correct. One could write something like:
This sort of structure may work for a small number of choices, however when there are numerous choices – Wimp Poll events, or handling function keypresses, for instance – a CASE structure is much tidier.
The answer is staring you straight in the face. The example in the BBC manual on page 85:
Here, the logical TRUE/FALSE is implied. You are looking at the result of GET$. The first test, is it a “L” or an “l”? There will only be a yes or no answer here. Either it is an upper or lower case letter L, or it is not. If it is, then this is a logically TRUE statement, so the stuff that follows is executed. If not, we progress to the next line. The next example in the BBC guide uses integers, opening the CASE statement with the value of the guess% variable. The first test is to see if guess% is equal to X%. If it is, you win. The next test is to see if it is one less than or more than X%. If it is, you were oh-so-close. Otherwise, you don’t win. The format is a little different because rather than perform a direct comparison of something like you do with an IF statement, you specify what you want to check in the original CASE statement; and then each line basically checks whatever value is that line against that provided in the beginning. Like this:
Granted, the two examples given are a bit crap. There’s another, arguably better, example on page 72 of dealing with keyboard input (under the section Inputting data).
Now, now. Here’s one to consider. CASE is great when you have a simple known list of values that you wish to compare this with that. Poll events, Wimp messages, key press codes, ANSI colour codes, whatever. The book will tell you what CASE is. The other real-life examples and numerous programs will instruct you on how and when to use CASE. |
GavinWraith (26) 1563 posts |
Rick makes a good point. A manual for BASIC cannot be expected to be a tutorial on programming in general. The trouble with huge long IF THEN ELSEIF … statements is that they are hard to read (spaghetti logic) and sometimes involve unnecessary computation. The ideal is a program whose compiled code has no conditionals at all, only jumps. In the case of the Wimp_Polling loop, for example, the conditional bit is really only an artefact of the programming language used. In the RiscLua task library because once you know the reason code you know exactly which handler to use. No testing of values is required. In fact the wimp is designed so that the whole dialogue between the wimp and the user’s program can operate in this fashion.
|
Steve Fryatt (216) 2105 posts |
Is it any worse than some nested In the case of
vs.
doesn’t the |
Clive Semmens (2335) 3276 posts |
Unless it’s in a bit of code that gets executed one hell of a lot, this is a matter of no consequence whatsoever. And if part of your program is that heavily used, why isn’t that bit being done in assembler? |
Steve Drain (222) 1620 posts |
A bit like the Basalt extensions to BASIC where you can do:
and the function is called whenever the event occurs. |
Steve Drain (222) 1620 posts |
No, because that is poor, too, for the same reason that you give. Both are exacerbated by including a lot of code in the clauses, between WHENs or THEN|ELSE|ENDIF. It is the search for the next clause or ENDCASE|ENDIF that is the poor bit. But a single-line IF is relatively much quicker.
Because you exit the PROC, that is also so in the IF example. In that particular example I doubt there is any advantage, but a long list of conditions might. The |
Steve Drain (222) 1620 posts |
But I have seen many examples of just that, where there is a |
Steve Drain (222) 1620 posts |
With tongue-in-cheek, might I suggest |
Rick Murray (539) 13851 posts |
<stir> If I wanted fast, I’d be using a |
Steve Drain (222) 1620 posts |
This is marginally relevant to that observation. Some years back nemo and I had a lengthy discusion of how to speed up |
Clive Semmens (2335) 3276 posts |
Surely – but is the loop being executed millions of times? Could be, if it’s in a sorter with a big list to sort, say – and that really would be worth writing in Assembler. The example I gave is in a loop – but it only goes round the loop once per WIMP poll. If every app being polled did that, it really wouldn’t matter a toss (unless you had say ten thousand apps running at once…) |
Steve Drain (222) 1620 posts |
A lot of poor BASIC programming practices do not really matter, because speed is rarely relevant in a desktop user environment. Nevertheless, I hate to see memory and speed used recklessly. ;-) |
Clive Semmens (2335) 3276 posts |
That’s a different issue, though. It’s not poor practice to use CASE where it improves clarity, even if it hits speed – unless speed is actually important in a particular case. It very rarely is. Whereas clarity is generally important. (I’m a fine one to talk: my code is often as clear as mud.) |
David R. Lane (77) 766 posts |
Some replies to the above. @Rick
I did resalise this, but we are talking about manuals where readers are meeting the CASE statement for the first time. Surely the first examples should include those that refer transparently to conditions which are either true or false. It is a mistake to assume learners will be able to sort this out rather than be put off. Your example starting CASE GET% OF ….. only repeats what I have already referred to in the manuals. I am being called for dinner, will read more of your long post later. |
David R. Lane (77) 766 posts |
I have read through the rest of Rick’s ‘tutorial’, but it doesn’t answer, as some others have, the situation I described. Rick, thanks for your long explanation that you must have spent a long time writing, but I don’t need an explanation of the examples that I have already understood in the manuals. My first thought on the CASE TRUE OF … method, is that it could look messy if the condition following WHEN is complicated; but, yes, I must look at it further. |
Clive Semmens (2335) 3276 posts |
I think |
Rick Murray (539) 13851 posts |
First up, this:
This might have made sense when programming a 6502. On the ARM, even the ARM2, taking a jump means two instruction cycles are lost (as the pipeline must refill). On later ARMs, longer pipelines but predictive execution. Still, if the processor has conditionals at zero cost, surely the ideal is a program whose compiled code has conditionals and no jumps? Now to the X% = TRUE Y% = FALSE Z% = TRUE CASE TRUE OF WHEN X% : PRINT "X" WHEN Y% : PRINT "Y" WHEN Z% : PRINT "Z" ENDCASE Will only print “X”, not “X” and “Z”. Again, once you understand that you are performing a comparison between the statement given to the CASE command (whatever it may be) and the statement(s) given in each WHEN command, it does become a simple logic test. Either it matches, or it doesn’t. Leaving you to ponder if CASE TRUE OF was devised just to troll people. :-) |
Rick Murray (539) 13851 posts |
Speaking of trolling, let me provide an example of something that is proper BASIC accepted by the interpreter, but is insane troll logic. X% = 1 PRINT FNa, FNb, FNc END DEFFNa X% += 1 DEFFNb X% += 1 DEFFNc X% += 1 =X% Does this: >RUN 4 6 7 > This is much akin to CASE TRUE OF. It is valid because it is logically sound and accepted by the interpreter. It is, however, horrible code for the two obvious reasons (function modifying a global and a function with three entry points). I’m inclined to think that anybody who writes code like that probably ought to rethink their algorithm instead of trying clever tricks… |