I want to implement the Reverse Polish notation. In order to do that, I have to implement a table which stores priority for operators, as such:
operator
priority
(
0
+, -
1
*, /
2
How do I do that?
Since you have a fixed-number of key-value pairs that are known at compile time, you don't need the dynamic nature and overhead of std::unordered_map. You can use a simple switch statement, which is static and harder to get wrong:
int get_priority(char const op) {
switch (op) {
case '(':
return 0;
case '+':
case '-':
return 1;
case '*':
case '/':
return 2;
default:
return -1; // handle the error
}
}
Related
I am writing a Python Library in C++ using the python C Api. There I have about 25 functions, that all accept two strings. Since Python might save strings in utf8/16/32 (the moment on char requires a bigger size the whole string will use the bigger size). When checking which kind the string is you get a enum value between 0 and 4. 0/4 should be handled as utf32, 1 as utf8 and 2 as utf16. So I currently have a nested switch for each combination:
The following example shows how the elements are handled in my code. random_func is different for each of my functions and is a template, that accepts a string_view of any type. This way to write the code results in about 100 lines of boilerplate for each function that accepts two strings.
Is there a way to handle all these cases without this immense code duplication and without sacrificing performance?
double result = 0;
Py_ssize_t len_s1 = PyUnicode_GET_LENGTH(py_s1);
void* s1 = PyUnicode_DATA(py_s1);
Py_ssize_t len_s2 = PyUnicode_GET_LENGTH(py_s2);
void* s2 = PyUnicode_DATA(py_s2);
int s1_kind = PyUnicode_KIND(py_s1);
int s2_kind = PyUnicode_KIND(py_s2);
switch (s1_kind) {
case PyUnicode_1BYTE_KIND:
switch (s2_kind) {
case PyUnicode_1BYTE_KIND:
result = random_func(
basic_string_view<char>(static_cast<char*>(s1), len_s1),
basic_string_view<char>(static_cast<char*>(s2), len_s2));
break;
case PyUnicode_2BYTE_KIND:
result = random_func(
basic_string_view<char>(static_cast<char*>(s1), len_s1),
basic_string_view<char16_t>(static_cast<char16_t*>(s2), len_s2));
break;
default:
result = random_func(
basic_string_view<char>(static_cast<char*>(s1), len_s1),
basic_string_view<char32_t>(static_cast<char32_t*>(s2), len_s2));
break;
}
break;
case PyUnicode_2BYTE_KIND:
switch (s2_kind) {
case PyUnicode_1BYTE_KIND:
result = random_func(
basic_string_view<char16_t>(static_cast<char16_t*>(s1), len_s1),
basic_string_view<char>(static_cast<char*>(s2), len_s2));
break;
case PyUnicode_2BYTE_KIND:
result = random_func(
basic_string_view<char16_t>(static_cast<char16_t*>(s1), len_s1),
basic_string_view<char16_t>(static_cast<char16_t*>(s2), len_s2));
break;
default:
result = random_func(
basic_string_view<char16_t>(static_cast<char16_t*>(s1), len_s1),
basic_string_view<char32_t>(static_cast<char32_t*>(s2), len_s2));
break;
}
break;
default:
switch (s2_kind) {
case PyUnicode_1BYTE_KIND:
result = random_func(
basic_string_view<char32_t>(static_cast<char32_t*>(s1), len_s1),
basic_string_view<char>(static_cast<char*>(s2), len_s2));
break;
case PyUnicode_2BYTE_KIND:
result = random_func(
basic_string_view<char32_t>(static_cast<char32_t*>(s1), len_s1),
basic_string_view<char16_t>(static_cast<char16_t*>(s2), len_s2));
break;
default:
result = random_func(
basic_string_view<char32_t>(static_cast<char32_t*>(s1), len_s1),
basic_string_view<char32_t>(static_cast<char32_t*>(s2), len_s2));
break;
}
break;
}
Put the complexity away in a function using variants
using python_string_view = std::variant<std::basic_string_view<char>,
std::basic_string_view<char16_t>,
std::basic_string_view<char32_t>;
python_string_view decode_python_string(python_string py_str)
{
Py_ssize_t len_s = PyUnicode_GET_LENGTH(py_str);
void* s = PyUnicode_DATA(py_str);
int s_kind = PyUnicode_KIND(py_str);
switch (s_kind) {
//return correct string_view here
}
}
int main()
{
python_string s1 = ..., s2 = ...;
auto v1 = decode_python_string(s1);
auto v2 = decode_python_string(s2);
std::visit([](auto&& val1, auto&& val2) {
random_func(val1, val2);
}, v1, v2);
}
I'm unsure about the performance though.
For what it is worth:
The difference it makes to have different char types is at the moment you extract the character values inside random_func (requiring nine template specializations, if I am right).
You would be close to a solution by fetching the chars in all cases using the largest type and masking out or shifting out the extra bytes where necessary. Instead of templating, you would pass a suitable mask and a stride information. Something like
for (char32_t* c= (char32_t*)s1; c &= mask, c != 0; c= (char32_t*)((char*)c + stride))
{
…
}
Unfortunately, not counting the extra masking operation, you hit a wall because you may have to fetch too many bytes at one end of the string, causing an illegal memory access.
I'm learning about recursions and I have to later apply it a project. My project is going to include a deck of cards and I'm just curious if this counts as recursion? To me it seems like it since it calls itself. However since i'm still learning I want to make sure i'm understanding the concept and i'm not wrong. Please let me know.
string Card::suitName(Suit S) {
string suits;
switch (S) {
case clubs:
suits = "Clubs";
break;
case diamonds:
suits = "Diamonds";
break;
case hearts:
suits = "Hearts";
break;
case spades:
suits = "Spades";
break;
}
return (suits);
}
// declare the value of the cards
string Card::cardValue(Value Card) {
string card;
switch (Card) {
case two:
card = "Two";
break;
case three:
card = "Three";
break;
case four:
card = "Four";
break;
case five:
card = "Five";
break;
case six:
card = "Six";
break;
case seven:
card = "Seven";
break;
case eight:
card = "Eight";
break;
case nine:
card = "Nine";
break;
case ten:
card = "Ten";
break;
case jack:
card = "Jack";
break;
case queen:
card = "Queen";
break;
case king:
card = "King";
break;
case ace:
card = "Ace";
break;
}
return (card);
}
You've defined two functions Card::suitName() and Card::cardValue(). Neither function calls itself or any other user-defined function (excluding any std::string functions). There's no recursion anywhere in your code.
Recursion is defined as a function calling itself within itself. So suitName would need to have a call to suitName or cardValue to cardValue. Neither make such a call, so
There is no recursion in your code
Recursion requires a function to call itself (most commonly directly but it can be via another function).
Example:
bool doesHandContainAceRecursion(std::vector<Card> const& playersCards, int pos) {
// If we have tried all the cards and gone past the end
// Then return false.
//
// This is a classic part of recursion, checking if you have reached the end.
if (pos >= playersCards.size()) {
return false;
}
// Otherwise do the work you want to do.
if (playersCards[pos].value == 1) {
return true;
}
// This is the recursive call.
// It calls itself. This is a classic example of tail recursion.
return doesHandContainAceRecursion(playersCards, pos + 1);
}
// This is the function you call and it sets up a call to the
// recursive function above.
bool doesHandContainAce(std::vector<Card> const& playersCards) {
return doesHandContainAceRecursion(playersCards, 0);
}
No this is not recursions. Recursions is when method is calling itself
Example:
string Card::suitName(Suit S) {
string suits;
switch (S) {
case clubs:
suitName(diamonds); // Recursions
break;
case diamonds:
suits = "Diamonds";
break;
}
return (suits);
}
No, the code you provided does not involve recursion. Recursion is a programming technique where a function calls itself, either directly or indirectly, to solve a problem. In the code you provided, there are no function calls that reference the function itself.
The functions suitName and cardValue are simply taking in an input and returning a corresponding output based on a switch statement. They do not involve any kind of iterative or recursive process to arrive at the output.
In the context of a deck of cards, you might consider using recursion for a function that shuffles the cards, as shuffling involves repeatedly swapping pairs of cards until the deck is randomized. However, the code you provided does not involve recursion.
Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 5 years ago.
Improve this question
I just solved 1st exercise on learncpp, chaper 7.8. I tested their version of switch loop and it works, but I wonder it's without break, shouldn't in this case such code cause undesired behavior? When can I leave out break?
Here is my version(which also works correctly):
switch (op)
{
case '+': {
return add;
break;}
case '-': {
return subtract;
break;}
case '*': {
return multiply;
break;}
case '/': {
return divide;
break;}
default: ;
}
Without break, the flow of execution will simply continue to the next case - unless you jump in some other way of course (like returning, in your case). This is known as fall-through.
Whether this is a bug or not depends on the program. It can be useful if (for example) you want multiple cases to have the same action:
switch (op)
{
case '+':
return add;
break; // these breaks do nothing - we have already returned!
case '-':
return subtract;
break;
case 'x': // fall through to '*'
case '*':
return multiply;
break;
case '/':
return divide;
break;
}
Note that C++ does not insist on your using break, but often switch cases depend on them, to obviate the useful follow through feature.
You can remove a break with no effect on your program if program control never reaches it: None of the break statements after the return statements are reachable, so you can safely remove them.
You also don't need an empty default label either.
Since you are new to C++, consider that:
Anytime your code reaches a return statement, your current function/method will end and return the specified valued without executing any more line of codes. So your break statements are never going to be reached.
When a break is reached, the current switch, while or for will end. And the flow will continue outside of its scope {...}.
If you do not use break in a switch case then the flow just continues (even to the next case).
It may be useful to you to understand what a switch is doing under the covers.
essentially this:
enum command {
add, subtract, multiply, divide, error
};
command foo(char op)
{
switch (op)
{
case '+': {
return add;
break;}
case '-': {
return subtract;
break;}
case '*': {
return multiply;
break;}
case '/': {
return divide;
break;}
default:
break;
}
return error;
}
Is logically the same as this:
command foo_that_will_give_people_seizures(char op)
{
if (op == '+') goto plus_;
if (op == '-') goto minus_;
if (op == '*') goto mpy_;
if (op == '/') goto div_;
goto break_;
plus_:
return add;
goto break_; // notice how this statement cannot be reached.
minus_:
return subtract;
mpy_:
return multiply;
div_:
return divide;
break_:
;
return error;
}
and the statement break; appearing in any of the cases in the first function is equivalent to goto break_ in the second.
ps. you won't win any friends writing code like this. People like to pretend that goto is a bad thing in c++ ;-)
You should be aware that break should be left out on default: (if the default is the last case) where the code flow basically reaches the end and doesn't need to get break.
The very purpose of break is to prevent code to continue to next case(s) when the actual case is true. return however, returns the given statement in the case and breaks the switch. If you are considering to return inside switch cases, then instead of using break, you may use return foobar; It will break by returning whatever you wish to return.
You can display a Value's type like this:
cout << val.type() << end;
and it print a number.
How can I map this number back to the actual type?
besides peeking in the header file, of course, which reveals all...
enum Value_type {
obj_type,array_type,str_type,bool_type,int_type,real_type,null_type
};
Nope, that seems to be the canonical way:
switch(v.type()) {
case obj_type: pp_obj(v, lev+1); break;
case array_type: pp_array(v, lev+1); break;
case str_type: pp<string>(v, lev+1); break;
case bool_type: pp<bool>(v, lev+1); break;
case int_type: pp<int>(v, lev+1); break;
case real_type: pp<double>(v, lev+1); break;
case null_type: pp_null(v, lev+1); break;
}
Value val;
read(is, val);
Object o = val.get_obj();
Then create a Pair, assuming it's type 0.
Pair pair = o[1];
where 1 is the iterated value. This took me forever to figure out, so I'm trying to save the time of anyone else who needs to look this up later. use sizeof(o)/sizeof(int) to iterate, along with the ++i instead of i++.
Ok, I'm new at C++. I got Bjarne's book, and I'm trying to follow the calculator code.
However, the compiler is spitting out an error about this section:
token_value get_token()
{
char ch;
do { // skip whitespace except '\n'
if(!std::cin.get(ch)) return curr_tok = END;
} while (ch!='\n' && isspace(ch));
switch (ch) {
case ';':
case '\n':
std::cin >> WS; // skip whitespace
return curr_tok=PRINT;
case '*':
case '/':
case '+':
case '-':
case '(':
case ')':
case '=':
return curr_tok=ch;
case '0': case '1': case '2': case '3': case '4': case '5':
case '6': case '7': case '8': case '9': case '.':
std::cin.putback(ch);
std::cin >> number_value;
return curr_tok=NUMBER;
default: // NAME, NAME=, or error
if (isalpha(ch)) {
char* p = name_string;
*p++ = ch;
while (std::cin.get(ch) && isalnum(ch)) *p++ = ch;
std::cin.putback(ch);
*p = 0;
return curr_tok=NAME;
}
error("bad token");
return curr_tok=PRINT;
}
The error it's spitting out is this:
calc.cpp:42: error: invalid conversion from ‘char’ to ‘token_value’
token_value is an enum that looks like:
enum token_value {
NAME, NUMBER, END,
PLUS='+', MINUS='-', MUL='*', DIV='/',
PRINT=';', ASSIGN='=', LP='(', RP=')'
};
token_value curr_tok;
My question is, how do I convert ch (from cin), to the associated enum value?
You can't implicitly cast from char to an enum - you have to do it explicitly:
return curr_tok = static_cast<token_value> (ch);
But be careful! If none of your enum values match your char, then it'll be hard to use the result :)
Note that the solutions given (i.e. telling you to use a static_cast) work correctly only because when the enum symbols were defined, the symbols (e.g. PLUS) were defined to have a physical/numeric value which happens to be equal to the underlying character value (e.g. '+').
Another way (without using a cast) would be to use the switch/case statements to specify explicitly the enum value returned for each character value, e.g.:
case '*':
return curr_tok=MUL;
case '/':
return curr_tok=DIV;
You need an explicit cast:
curr_tok = static_cast<token_value>(ch);
The reason is that it's dangerous to convert an integer type to an enum. If the value is not valid for the enum then behaviour is undefined. So the language doesn't let you do it accidentally with an implicit conversion. The explicit conversion is supposed to mean "I know what I'm doing, and I've checked that the value is valid".
I think I wouldn't try to explicitly set the values of the enum symbols and instead write a case for every symbol that in your switch statement. Doing it that way will probably be harder to debug if something goes wrong and the performance cost writing a case for every symbol is so low, that it's not even worth considering (unless you're writing for some kind of extremely low-end embedded system and probably still not worth it).
return curr_tok=(token_value)ch;