So basically my switch case works but my professor says there are too many returns and "use a variable result, then return it at the end!"
so here is my code
int getMonthValue(){
switch(month){
case(1): //January
if(isLeapYear(year) == true)
return 6;
else
return 0;
case(2): // February
if(isLeapYear(year) == true)
return 2;
else
return 3;
case(3): //March
return 3;
case(4): //April
return 6;
case(5): //May
return 1;
case(6): //June
return 4;
case(7): //July
return 6;
case(8): //August
return 2;
case(9): //September
return 5;
case(10): //October
return 0;
case(11): //November
return 3;
case(12): //December
return 5;}
};
I dont see anything wrong with it, I am sure it could be written better. Would someone be able to show me a way to either format this in a more user friendly way? My professor also wanted me to use breaks in the switch instead, not sure why to use the break over return.
This is not good idea to use logical operators in your case. Use a array! This code is very well understood and it is very fast. And it is very easy to change the return value:
unsigned getMonthValue(unsigned month, unsigned year) {
const static unsigned ans[2][12] = {
// Jan F M A M J J A S O N Dec
{0, 3, 3, 6, 1, 4, 6, 2, 5, 0, 3, 5 } //normal year
, {6, 2, 3, 6, 1, 4, 6, 2, 5, 0, 3, 5 } //leap year
};
assert(month <= 12);
assert(month >= 1);
const size_t month_index = month - 1;
const size_t leap = isLeapYear(year) ? 1 : 0;
return ans[leap][month_index];
}
UPDATE
There is very useful link about this technic - Lookup_table. Thanks to Schwern!
Try this
int getMonthValue(int month, int year){
map<int,int> staticAnswers;
staticAnswers[3] = 3;
staticAnswers[4] = 6;
staticAnswers[5] = 1;
staticAnswers[6] = 4;
staticAnswers[7] = 6;
staticAnswers[8] = 2;
staticAnswers[9] = 5;
staticAnswers[10] = 0;
staticAnswers[11] = 3;
staticAnswers[12] = 5;
switch(month){
case(1): //January
if(isLeapYear(year) == true)
return 6;
else
return 0;
case(2): // February
if(isLeapYear(year) == true)
return 2;
else
return 3;
default:
return staticAnswers[month];
};
Here is how I would write it:
int
getMonthValue (int month, int year)
{
switch(month)
{
case 1: // January
if (isLeapYear(year))
return 6; // explanation here of why 6 is the right value
return 0;
case 2: // February
if (isLeapYear(year))
return 2; // explain why 2 is the right value
return 3;
case 3: return 3; // March
case 4: return 6; // April
case 5: return 1; // May
case 6: return 4; // June
case 7: return 6; // July
case 8: return 2; // August
case 9: return 5; // September
case 10: return 0; // October
case 11: return 3; // November
case 12: return 5; // December
}
}
I know many inexperienced programmers would object to the statements not being consistent. However—written this way—the statement structure exactly follows the logic. That is, the lexical structure reveals the logic.
A good thing about switch statement is that you are allowed to fall-through case statements, so this is allowed in C++:
switch (month) {
case 4:
case 7:
return 6;
case 9:
case 12:
return 5;
Which should reduce the amount of return statements but will change the readable order.
Another option, since you are checking for all values in range [1-12], would be to use an array, eg:
static const int values[] = { 3, 6, 1 ... };
if (month < 3) {
/* leap year check */
}
else
return values[month - 3]; // we subtract 3 to start from March
But this in the end should be mostly personal preference, so the suggestion given by the professor sounds like just something to nag you.
Your professor is strictly enforcing the "Single Entry, Single Exit" (SESE) rule of Structured Programming. All inputs come in as arguments. All output is via a single return statement at the end. No surprise returns in the middle of the routine. No globals. Minimal side effects. No gotos. It makes code easier to read when you don't have to carefully scan it for returns or calling gotos in the middle of a function.
This is a good rule of thumb. And at the time this was conceived, in the 1970s, this was a radical idea. Now it's just how you do things.
The problem, like many style rules, is when you strictly enforce the rule without considering why the rule exists; you can wind up with the opposite effect. Some of the answers here have illustrated that nicely. Style rules are necessarily simplified versions of reality and enumerating all the edge cases and exceptions is difficult. Gotta allow some wiggle room.
For a small routine like yours that does one very clear thing, fits on one page, and is one easy to read switch statement... just do multiple returns like you're doing. Storing a return value for the end would make it longer and more complicated defeating the point of the rule. And you avoid forgetting a break, a very common C pitfall.
The other good reason to violate SESE is early exit. Structured programming says to do this:
int some_function(int arg) {
int ret;
if( arg < 0 ) {
ret = arg;
}
else {
this is the real
meat and potatoes
of the function
and because of
that one simple
condition at the
top it all
is indented an
extra level
adding complexity
to the whole
function
ret = whatever;
}
return ret;
}
Every level of nesting adds complexity. Early exit avoids this unnecessary nesting by getting the simple cases out of the way at the top.
int some_function(int arg) {
if( arg < 0 ) {
return arg;
}
this is the real
meat and potatoes
of the function
which doesn't
have to be indented
an extra level
since we dealt with
all the simple cases
at the top
return whatever;
}
The extra return at the start doesn't significantly add complexity because it's in a predictable spot. It's only surprise returns in the middle of a large function that are a problem.
The other side of this is minuscule style arguments like this can distract from real problems. Your code has two much bigger problems, and I hope your prof called you on them.
Pass values into functions, don't use globals.
Your function takes no arguments. Instead month and year are globals. This is very bad style. It makes your program difficult to understand because anything can potentially effect anything. Instead, pass them in.
int getMonthValue( int month, int year ) {
...
}
Check your bounds.
The second problem is your function has no bounds checking. What if month is 0? 13? -50? 20398? It will fall through the switch statement and out the bottom of the function. The compiler should have warned you about this.
test.c:48:1: warning: control may reach end of non-void function [-Wreturn-type]
}
^
To solve this, unless you can think of a good reason not to, always put a default clause on switch.
switch(month) {
...all your regular cases...
default:
fprintf(stderr, "Month %d is out of range.\n", month);
exit(1);
}
Your professor is right, don't worry its all in the learning process
Try something like this:
int result = -1;
switch(month){
case 1: //January
if(isLeapYear(year) == true)
result = 6;
else
result = 0;
break;
case 2: // February
if(isLeapYear(year) == true)
result = 2;
else
result = 3;
break;
case 3: //March
result = 3;
break;
case 4: //April
result = 6;
break;
case 5: //May
result = 1;
break;
case 6: //June
result = 4;
break;
case 7: //July
result = 6;
break;
case 8 : //August
result = 2;
break;
case 9: //September
result = 5;
break;
case 10: //October
result = 0;
break;
case 11: //November
result = 3;
break;
case 12: //December
result = 5;
break;
}
return result;
Also don't use brackets in your cases it doesnt look clean at all.
I sometimes also choose to use brackets in my cases "{" and "}" although in this example its probably more appropiate not to. Weather or not using brackets is the best practice I do not know.
I am refeering to these brackets by the way "{" and "}"
Anyway thats a cleaner case statement, hope it helped.
a possible solution With the ternary operator!:
int getMonthValue()
{
return
month == 3 ? 3 :
month == 4 ? 6 :
month == 5 ? 1 :
month == 6 ? 4 :
month == 7 ? 6 :
month == 8 ? 2 :
month == 9 ? 5 :
month == 10 ? 0 :
month == 11 ? 3 :
month == 12 ? 5 :
month == 1 ? isLeapYear(year) ? 6 : 0 :
month == 2 && isLeapYear(year) ? 2 : 3 ;
}
Related
I recently started on Codecademy, on the subject about conditions right now. I'm attempting to use both if + if else and switch + break + default. On the last step of this exercise i encountered this.
Can we use case X>y?
int max = 0;
std::string house;
switch(max){
case gryffindor > max : max = gryffindor;
house = "gryffindor";
case hufflepuff > max : max = gryffindor;
house = "hufflepuff";
case ravenclaw > max : max = gryffindor;
house = "revenclaw";
case slytherin > max : max = gryffindor;
house = "slytherin";
}
switch(X) is a "jump to value" shortcut. It's in the language because a very simple and effective optimization exists to implement this.
For this optimization to work, the possible value must be static and known at compilation time. So you can not have a dynamic/runtime Y in your case Y: destination.
The optimization is doing this way (simplified):
Let's say you have a value that can go from 0 to 9.
Create a switch on this value and 10 cases from 0 to 9.
The compiler will create a table containing 10 entries (which are the address of the code in memory for each case)
When testing the value, the compiler only have to jump to the address contained in the table at position value, unlike following a long if/else test chain.
This will not work in your case, you'll have to create a chain of if/else like this:
enum House { gryffindor, hufflepuff, ravenclaw, slytherin };
std::string getHouse(House house, int & max) {
max = 0;
if (house == gryffindor) max = gryffindor;
else if (house == hufflepuff) max = hufflepuff;
else if (house == ravenclaw) max = ravenclaw;
else max = slytherin;
switch(max){
case gryffindor: return "gryffindor";
case hufflepuff: return "hufflepuff";
case ravenclaw : return "revenclaw";
case slytherin : return "slytherin";
default: return "none";
}
}
If you are not returning after a case, you must use break; to exits the switch block.
This question already has answers here:
Function with missing return value, behavior at runtime
(4 answers)
Closed 5 years ago.
I was helping a friend with one of his C++ assignments and we found the following code snippet would throw exceptions in MSVC, but when compiling with G++, the exact same code would work fine. The exceptions were return because this function called getValue() wasn't returning anything.
string getValue(int value) {
ostringstream convert;
string rtnValue;
switch (value) {
case 11:
{
rtnValue = "J";
break;
}
case 12:
{
rtnValue = "Q";
break;
}
case 13:
{
rtnValue = "K";
break;
}
case 14:
{
rtnValue = "A";
break;
}
default:
{
//
// if the value is a a number, we assume it is 2..10
//
convert << value; // use a stream to convert the number
rtnValue = convert.str(); // into a string
if (value < 2 || value > 10)
{
rtnValue = "ERROR" + rtnValue + "ERROR";
}
}
return rtnValue;
}
}
This program turns integers into strings. For the numbers 11-14 it uses switch statement (I know this isn't the best implementation but it's an introductory class).
We found that this could easily be solved by adding another return statement at the end.
string getValue(int value) {
ostringstream convert;
string rtnValue;
switch (value) {
case 11:
{
rtnValue = "J";
break;
}
case 12:
{
rtnValue = "Q";
break;
}
case 13:
{
rtnValue = "K";
break;
}
case 14:
{
rtnValue = "A";
break;
}
default:
{
//
// if the value is a a number, we assume it is 2..10
//
convert << value; // use a stream to convert the number
rtnValue = convert.str(); // into a string
if (value < 2 || value > 10)
{
rtnValue = "ERROR" + rtnValue + "ERROR";
}
}
return rtnValue;
}
return rtnValue;
}
And this now fixes it for MSVC (and I assume G++ if I checked).
Why did that fix work? Does MSVC and G++ treat parentheses differently with respect to switch statements?
In the first example, the return rtnValue is in the wrong place, and will only ever work when the default case is hit.
In the second example, you have added the return rtnValue in the correct place (and the other can be safely removed).
As to why it worked on GCC and not on MSVC, I don't know, without the return being in the correct place, it's not valid C++ (not all paths have a return value), so you should have got a compilation error on any C++ compiler.
I would suggest the problem is actually the way the braces {} are being used, and your friend thought that the closing brace of the default case, actually closed the switch statement, but it doesn't.
Also, there is no need to have braces on any of the case statements. Braces CAN be used in this way to introduce scoping (for example, temporary variables for a particular case), but in your example, just leads to confusion.
this is the problem
default:
{
convert << value; // use a stream to convert the number
rtnValue = convert.str(); // into a string
if (value < 2 || value > 10)
{
rtnValue = "ERROR" + rtnValue + "ERROR";
}
}
return rtnValue;
}
your return statement is in the wrong block, i.e , switch block.
what happens is that, when a case is satisfied it breaks out of the switch that is why it didn't return anything (because it is now out of switch statement).
In order to fix it you have to move your return statement to out of the switch statement to the end of the function.
This correction will we equivalent to the second code that you have provided.
But even in the second code remove the inner return statement.
Return value
Your return statement in the first sample applies to the default case only since the execution of the switch block ends with a break statement in every other case.
In a non-default case, you leave the return value of your function uninitialized. MSVC does warn about that while debugging (see https://learn.microsoft.com/en-us/visualstudio/debugger/how-to-use-native-run-time-checks for details) but GCC does not. This problem might be detected during compile time but you cannot rely on that.
The return statement added to the second sample is correct. You can remove the original one which becomes superfluous.
Braces
Notice that the braces inside the switch block are not necessary and introduce confusion here. They would be only useful if you created a local variable just to be used in a single case. Anyway, the braces should be indented more than the braces of the switch block. This part
}
return rtnValue;
}
demonstrates the misleading indentation clearly. The indentation used in the second example is one of the good solutions to this problem.
I'm 99% sure this won't work but that remaining 1% is bothering me
int x;
//is this if statement
if(x == 1, 5, 7)
{
//do something here
}
//equivalent to this if statement
if((x == 1) || (x == 5) || (x == 7))
{
//do something here
}
No it's totally not equivalent.
if(x == 1, 5, 7)
calls the comma operator, which will effectively end up in the last value because of , has the lowest precedence:
if(7)
since unfolding with parenthesis should look like
if(((x == 1), 5), 7)
while
if((x == 1) || (x == 2) || (x == 7))
checks if x equals either 1, 2 or 7.
They are not equal. When you write it like
if(x == 1, 5, 7)
{
//do something here
}
it basically translates into
if(7)
{
//do something here
}
which will always be true in case the number in the condition block is a non-zero number.
Example 1:
int main()
{
int x=10;
if(x==1,5,7)
cout<<"hello"<<endl;
return 0;
}
Here, the output is "hello", because 7 is treated as a true boolean variable.
Example 2:
int main()
{
int x=10;
if(x==1,5,0)
cout<<"hello"<<endl;
return 0;
}
Here, there is no output because 0 is considered as a false boolean variable.
Regarding a faster solution discussed in the comment section of the OP, here's a 'fast' solution:
If you have a large number of constant comparisons to perform, a switch statement is faster than individual if(x == 1) statements as it is compiled to a branch-table (a kind of hashtable directly within program code, giving it O(1) lookup), however it's possible that existing compilers will already optimize if(x==1||x==2||x==3...) to a branch-table too.
bool xIsInSet = false;
switch( x ) {
case 0: case 1: case 2: case 3:
case 4: case 5: case 6: case 7: // add a case for each literal comparison
xIsInSet = true; // no `break` needed, there's only 1 case.
}
if( xIsInSet ) {
// do stuff
}
This can be inlined to a lambda which is invoked immediately to eliminate xIsInSet:
if( [&x]() -> bool {
switch(x) { case 0: case 1: case 2: case 3: return true; }
return false; }()
) {
// do stuff
}
Unfortunately C++11's variadic templates don't let us dynamically add case statements, and hacking it using a preprocessor #define is possible - if you don't mind using a metaprogramming library. A better alternative might be an inline #include of a file generated by your build script. What would be even neater would be a way to somehow #include the standard-output from another program (e.g. if we could do #include '.\generateCasesFor.sh 1 2 5 10 12', alas not yet).
I am writing a code to assign a scoring system to values of a card. I have a member function that takes an int and changes its value based on the scoring system. I can't seem to get it to output anything besides 10 :
int Obj::eval(int b)
{
switch (b)
{
case 0:
b = 11; //automatically assigns ace value of 11
case 1:
b = 2;
case 2:
b = 3;
case 3:
b = 4;
case 4:
b = 5;
case 5:
b = 6;
case 6:
b = 7;
case 7:
b = 8;
case 8:
b = 9;
case 9:
b = 10;
case 10:
b = 10;
case 11:
b = 10;
case 12:
b = 10;
}
return b;
}
Insert break at the end of each case. C's switch is "fall-through": if you don't prevent it, code just keeps executing next line: if b is 0, all the assignments will get executed, in order. break will jump out of the switch.
I.e. your code needs to look like this:
switch (b)
{
case 0:
b = 11; //automatically assigns ace value of 11
break;
case 1:
b = 2;
break;
/* ... */
A switch case should end with a break;. Otherwise there will be a fall through and all the subsequent cases will be executed.
Your code should look something similar to this.
switch(b)
{
case 0:
//bodyhere
break;
case 1:
//bodyhere
break;
}
It's important not to miss the break statements unless you intend to execute the following cases too.
You should always use the break statement after each case because C++ will continue to execute the next case. For example:
switch(b)
{
case 0:
// body
// body
break;
case 1:
// body
// body
break;
case 2:
// body
// body
break;
}
You can also use a default as a last case. However, you don't need a break statement with the default.
switch(b)
{
case 0:
// body
break;
case 1:
// body
break;
case 2:
// body
break;
default:
// body
}
Switch statements in C and C++ have a "feature" called fallthrough, where if you don't actually break out of the cases, execution will just continue through to the next case (thus always resulting in b receiving 10).
Add break statements after each case.
case 0:
b = 11; //automatically assigns ace value of 11
break;
case 1:
b = 2;
break;
case 2:
b = 3;
break;
// etc.
Languages derived from C where control moves to the matching case, and then execution continues or "falls through" to the statements associated with the next case in the source text. Should use a break to avoid it.
case 0:
b = 11;
break;
case 1:
b = 2;
break;
In your case return would be fine too,
case 0:
b = 11;
return b;
case 1:
b = 2;
return b;
In addition to the case fallthrough problem described above, there are more efficient means other than switch to map between values, such as std::map
static map<int, int> myMap = { { 0, 11 }, { 1, 2 }, { 2, 3 }, ... { 11, 10 }, { 12, 10 } };
int Obj::eval(int b)
{
return myMap[b];
}
You need to add a break statement at the end of each branch. Otherwise the flow of control will continue to the next branch.
Is it possible to branch code without using an if statement?
Yes, you can, GPU-style.
Say you have a function that branches, and returns a value at the end.
float function( float input )
{
if( input > 0 )
{
// do stuff
finalValue = 2+4+8*input;
return finalValue ;
}
else
{
// do other stuff
finalValue = 1+input;
return finalValue ;
}
}
To do this without branching, you can write the code GPU-style: that is, evaluate both branches, then throw away the one you don't want at the end.
float function( float input )
{
// do stuff..regardless
finalValue1 = 2+4+8*input;
// do other stuff..regardless
finalValue2 = 1+input;
bool resultMask = input > 0 ; // 1 if should use finalValue1.
return finalValue1*resultMask + finalValue2*(1 - resultMask) ;
}
So there you have it. Branching without branching, if statements without if statementing.
Depends on what you mean by "branch" and "if". Any of the below branch, with no "if".
switch (foo) {
}
Or ternary operators, if you don't count:
x == 0 ? doFunc1() : doFunc2()
If your language supports function pointers:
funcArray[selectedOption]()
You can be silly and do:
boolean once = true;
while (condition && once) {
doAWhichNeverReturns();
once = false;
}
doB();
But I don't think this really answers your question, because I don't know what you're trying to do.
I was thinking about that because in mindastry game there is no dynamic if-statements, its simple script.
If you know adresses, you can use a max function:
0: set adressFalse = 5;
1: set adressTrue = 7;
2: set boolean // 0 or 1
3: adress = max (adressTrue * boolean, adressFalse) // 7 or 5
4: goto adress
5: print("false");
6: goto 8
7: print("true");
8: // next code
Pay attention: goto input isnt a expression here.