I'm beginner to C++ and I have implemented the following simple jump table, but was wondering if I'm doing it the right way. Is there anyway I can improve the following code?
The following code is using a dictionary (I'm from a C# background) to store functions' pointers.
#include <cstdio>
#include <iostream>
#include <string>
#include <unordered_map>
using namespace std;
void Zero() { printf("Zero\n"); }
void One() { printf("One\n"); }
void Two() { printf("Two\n"); }
void Three() { printf("Three\n"); }
string prompt()
{
printf("Enter number from 0 to 3 or q to quit:\n");
string line;
getline(cin, line);
return line;
}
int main(int argc, const char * argv[]) {
unordered_map<string, void(*)()> map;
map["0"] = Zero;
map["1"] = One;
map["2"] = Two;
map["3"] = Three;
while (true) {
string c = prompt();
if (c == "q") break;
map[c]();
}
return 0;
}
How about a switch statement?
switch (c) {
case 0:
printf("Zero\n"); break;
case 1:
printf("One\n"); break;
case 2:
printf("Two\n"); break;
case 3:
printf("Three\n"); break;
default:
break;
}
There's not much you can do to make your code "faster" without going for the switch solution which breaks the original idea of having an array of functions. If you only gonna use 'characters' such as '0' => '9', 'a' => 'z' you could dodge the memory allocation needed for the string, and you could also initialize your map with an initializer_list, and you could also make such array const static if that's viable.
Here goes my "optimized" code if it helps.
inline char prompt() //this function will probably 900% be inlined even if you don't specify the inlike keyword
{
printf("Enter number from 0 to 3 or q to quit:\n");
char v;
while (!(std::cin >> v)); //Just to make sure we get valid input
return v;
}
int main()
{
static const std::unordered_map<char, void(*)()> mymap =
{
{ '0' , Zero },
{ '1' , One },
{ '2' , Two },
{ '3' , Three }
};
while(1)
{
auto it = mymap.find(prompt());
// Without this check, your program will crash if input is invalid.
if (it != mymap.end())
{
it->second();
break;
}
}
return 0;
}
Pleas provide more details for your case of efficiency. Do you mean memory/cpu cycles/pass-through?
According to your code:
it's not error prone (use auto it = map.find(key); function for searching and check output it != map.end() value, so no new elements will be created)
it's good enough for string key type
you case easily make more flexible by replacing function pointer with std::function<void()>
It terms of more low-level control you can you custom hash function and custom hash tables implementations.
On some data it may be usefull to consider std::map or sorted std::vector as an option.
As static lookup is fast, This will perform very good irrespective of compiler. Jump table differs from compiler to compiler. I would use following code, May be some people will object to this as global are bad. But before commenting, please evaluate this
string prompt()
{
printf("Enter number from 0 to 3 or q to quit:\n");
string line;
getline(cin, line);
return line;
}
enum Choice = {ZERO = 0, ONE, TWO, THREE};
static char *choice_str[] = {
"Zero",
"One",
"Two",
"Three"
};
int main(int argc, const char * argv[]) {
while (true) {
string c = prompt();
if (c == "q")
{
break;
}
else {
assert(atoi(c) >= Choice::ZERO && atoi(c) <=Choice::THREE);
printf("%s\n", choice_str[atoi(c)]);
}
}
Related
I'm trying to have a string only work if it matches an int in the list.
Code
int Keys[] = { 23454563, 1262352, 634261253, 152352 };
string key;
int main()
{
cout << ("Please Enter Key: ");
cin >> key;
if (key = Keys)
{
//Activate Code
}
else
{
//Don't activate
}
}
I've tried searching around and I can't find any valid methods. I did try
if (sscanf(key.c_str(), "%d", &Keys) == 1)
^this works, but any number works and that isn't what I'm looking for.
hmm. First, I don't know why you have to input a 'string' instead of an 'int'. Also, why do you make 'key' a global variable? Just put it inside 'main'. Moreover, 'Keys' is an array and you can't compare a variable with an array.You have to search through the array using a loop.
My prefer answer
#include <iostream>
int main()
{
constexpr int Keys[] { 23454563, 1262352, 634261253, 152352 };
int key;
bool isNumberMatched {false};
std::cout << ("Please Enter Key: ");
std::cin >> key;
for (auto number : Keys) //Search through Keys array
{
if (key == number)
isNumberMatched = true;
break;
}
if (isNumberMatched)
//Activate Code
else
//Don't activate
}
I have made a loop which should encrypt the phrases I tell it to, but didn't finish because of the problem. It should detect when I say "stop" in the console and shut down the loop. It doesn't work.
What i want it to do is to detect if i said stop and break the loop. I shouldn t get any random missfires from getting the letters s t o p from other words. As you can see, every time there is a letter out of order, it resets the vectors which locks all of the ifs until 'c' gets the correct letters in the correct order.
using namespace std;
int main()
{
char c,v[5];
int i=0;
while(i!=1)
{
cin.get(c);
if(c=='s' or v[1]=='s')
{
v[1]='s';
if(c=='t' or v[2]=='t')
{
v[2]='t';
if(c=='o' or v[3]=='o')
{
v[3]='o';
if(c=='p' or v[4]=='p')
{
v[4]='p';
v[1]=v[2]=v[3]=v[4]=0;
i=1;
}
else
v[1]=v[2]=v[3]=0;
}
else
v[1]=v[2]=0;
}
else
v[1]=0;
}
cout<<c;
if (i==1)
break;
}
return 0;
}
That should the work and is not indented hell code. It assumes that you are entering one character at a time.
#include <iostream>
int main(int argc, char const *argv[])
{
char keyword[] = "stop";
char* matching_char = keyword;
char char_from_user;
while(*matching_char != '\0')
{
std::cin.get(char_from_user);
// Reset if different character
if(*matching_char != char_from_user)
matching_char = keyword;
// Increment position of match
if(*matching_char == char_from_user)
++matching_char;
// Ignore rest in buffer
std::cin.ignore();
}
return 0;
}
Following your logic, you just need to assign the v array values after each if/else condition otherwise it will just get immediately reassigned to 0. For example, you first assign v[1] = 's', and then right after you assign it to v[1] = 0, because the if returns false in first iteration. The following code should solve the problem.
#include <iostream>
using namespace std;
int main()
{
char c,v[5];
int i=0;
while(i!=1)
{
cin.get(c);
if(c=='s' || v[1]=='s')
{
if(c=='t' || v[2]=='t')
{
if(c=='o' || v[3]=='o')
{
if(c=='p' || v[4]=='p')
{
v[4]='p';
v[1]=v[2]=v[3]=v[4]=0;
i=1;
}
else
v[1]=v[2]=v[3]=0;
v[3]='o';
}
else
v[1]=v[2]=0;
v[2]='t';
}
else
v[1]=0;
v[1]='s';
}
if (i==1)
break;
}
return 0;
}
I am trying to make a program that will use a switch statement and see if an element of a char array is a vowel and which one, but i am stuck at how to check the elements:
int prob2() {
char uName[25] = "";
int voCo = 0;
cout<<"Enter you first and last name, under 25 chars please: ";
cin>>uName;
int i = 0;
while(i <= 25){
switch(i){
case 1:
voCo++;
break;
case 2:
voCo++;
break;
case 3:
voCo++;
break;
case 4:
voCo++;
break;
case 5:
voCo++;
break;
default:
break;
}
i++;
}
cout<<"Your first and last name have: "<<voCo<<" vowels in them."<<endl;
return 0;
}
It seems you mean the following
#include <iostream>
#include <cctype>
using namespace std;
//...
size_t prob2()
{
const size_t N = 25;
char uName[N] = "";
size_t voCo = 0;
cout<<"Enter you first and last name, under " << N << " chars please: ";
cin.getline( uName, N );
for ( char *p = uName; *p != '\0'; ++p ) *p = toupper( ( unsigned char )*p );
for ( const char *p = uName; *p != '\0'; ++p )
{
switch( *p )
{
case 'A':
voCo++;
break;
case 'E':
voCo++;
break;
case 'I':
voCo++;
break;
case 'O':
voCo++;
break;
case 'U':
voCo++;
break;
default:
break;
}
}
cout<<"Your first and last name have: "<<voCo<<" vowels in them."<<endl;
return voCo;
}
You could try something like this:
const std::string vowels = "aeiou";
const std::string name = "martin luther king, jr.";
const unsigned int name_length = name.length();
unsigned int vowel_count = 0U;
for (unsigned int i = 0U; i < name_length; ++i)
{
if (vowels.find(name[i]) != std::string::npos)
{
++vowel_count;
}
}
No need for switch statement. This is one of many possible algorithms or implementations.
Edit 1: An array of counts
You could also use an array of counts:
unsigned int counts[26] = {0};
for (unsigned int i = 0U; i < name_length; ++i)
{
const c = std::tolower(name[i]);
if (isalpha(c))
{
counts[c - 'a']++;
}
}
const unsigned int vowel count =
counts['a'] + counts['e'] + counts['i']
+ counts['o'] + counts['u'];
First of all decouple user interaction from the logic solving your requirement. I think we can safely assume you can collect the input in this case and save it into an string. So we will not waste our time with that.
We will focus on developing and testing the code that solves the requirement. In a standard C++. Now here is the deep end of the pool. The code.
// mike.h
#pragma once
// std::string view requires C++17
#include <string_view>
// always use namespace,to avoid name clashes
namespace mike {
// make 'sv' the string_view literal available
using namespace std::string_view_literals;
// declare and define compile time
// string view literal
// 'constexpr' guarantees compile time
// notice the use of 'sv'
constexpr auto vowels = "eaiouEAIOU"sv;
// compile time function to count literals
// again 'constexpr' guarantees compile time
// inline gurantees we can include this header many times
// without making accidental duplicates of `count_vowels`
// 'in_' argument has 'std::string_view' passed by value
// pass by value is preferred standard C++ method
// of functions arguments passing
// 'std::string_view' is standard C++ preferred type
// to pass strings into functions
inline constexpr size_t
count_vowels(std::string_view in_)
{
// return type is size_t
// we can count very large number of vowels
// but all at compile time
size_t rezult{};
// this is C+17 'range for'
// we cast implicitly references to input elements
// from, `char const &` to `int const &`
// cost of that is very likely 0
for (int const & ch_ : in_)
for (int const & v_ : vowels)
// there is no if() here
// we simply add 0's or 1's, to the rezult
// false is 0, true is 1
// the correct by the book way of coding that is
// static cast from bool to int
// rezult += static_cast<int>( v_ == ch_ ) ;
rezult += v_ == ch_ ;
return rezult;
}
// runtime speed of this call is 0 (zero)
// all happens at compile time
// notice how we pass normal string literal
// no need to create string_view
constexpr size_t r1
= count_vowels("abra ca dabra");
// no runtime tests necessary
// `static_assert()` is compile time assert
// failure message is optional
static_assert(r1 == 5,
"compile time calculation failed, 'abra ca dabra', must contain 5 vowels");
} // mike ns
Hopefully there are a lots of comments. Solution does not use switch() statement or if() statements. Thanks to standard C++ constructs, code is very simple, resilient and probably very fast when compiled by modern optimizing compilers.
Solution works at compile time too. That is not stopping you to use it in your run-time scenario. Although, I would advise again using native char array. std::string might be a perfect match here.
std::string input_ = collect_user_input() ;
int rezult = count_vowels(input_);
Enjoy the standard C++ ...
This question already has answers here:
Why does C++ not have reflection?
(15 answers)
Closed 8 years ago.
I have a question about structures. I'm trying to have the user input a letter and have the the computer return two values. Here's what I have till now:
#include <iostream>
struct coords{
int coordsx1;
int coordsy1;
} a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p;
int main()
{
char first;
int coordsx1;
int coordsy1;
a.coordsx=0; e.coordsx=0; i.coordsx=0; m.coordsx=0;
b.coordsx=1; f.coordsx=1; j.coordsx=1; n.coordsx=1;
c.coordsx=2; g.coordsx=2; k.coordsx=2; o.coordsx=2;
d.coordsx=3; h.coordsx=3; l.coordsx=3; p.coordsx=3;
cin >> first;
coordsx1= first.coordsx; // this is the part that doesn't work
}
For example if the user inputs 'd' for the variable first, I want the computer to set the value of coordsx1 to be equal to 3 and coordsy1 to be equal to 0 (I haven't done the coordsy1 part yet).
Also, is this a good way to return more than one value for a user input?
What the user inputs in cin >> first is text, and it is stored in first, which is char. This is a variable that contains a character, not the variable whose name is the contained character, which is why it doesn't work.
C++ does not have the reflexive capabilities required to transform the text representation of a variable into the variable name, but you can do the conversion explicitly with chained ifs or, more conveniently, a switch:
switch(first)
{
case 'a':
coordsx1 = a.coordsx;
coordsy1 = a.coordsy;
break;
case 'b':
coordsx1 = b.coordsx;
coordsy1 = b.coordsy;
break;
case 'c':
coordsx1 = c.coordsx;
coordsy1 = c.coordsy;
break;
// All other cases
default:
std::cerr << "Wrong input" << std::endl;
}
Alternatively, you can simply put your data in an array and use the characters to index it, this also makes the data initialisation more readable:
#include <iostream>
struct Coords
{
int x;
int y;
};
Coords coords[3] = {
{ 1, 2 }, // a
{ 2, 3 }, // b
{ 3, 4 }, // c
};
int main(int arg, char** argv)
{
char first;
int coordsx1;
int coordsy1;
std::cin >> first;
if (first > 'c' || first < 'a')
{
std::cerr << "Wrong input" << std::endl;
return 1;
}
int index = first - 'a'; // Convert 'a' into 0, 'b' into 1, etc.
coordsx1 = coords[index].x;
coordsy1 = coords[index].y;
}
Ups :-)
You first have to learn that a character in runtime is not a variable definition from compile time!
What you get from cin is a character because you made
char first;
so if you want to select from this input your output, you have to write a cascade of case statements like:
switch ( first ) {
case 'a': coordsx1=a.coordsx; coordsy1=a.coordsy; break;
case 'b': coordsx1=b.coordsx; coordsy1=b.coordsy; break;
... a lot more ...
default: std::cerr << "Unknown value entered" << std::endl;
}
please have a look at std::vector or std::map. Both containers can help a lot in this use case!
I want to add '.' character besides another character in a string but I don't know how to do it ? is it possible?
#include <iostream>
#include <string.h>
using namespace std;
int main(int argc, char *argv[]) {
string input;
char dot='.';
cin>>input;
for(int i=0;i<input.length();i++)
{
if( input[i]>=65 && input[i]<=90)
{
input[i]=input[i]+32;
}
if( (input[i]=='a') || (input[i]=='e') || (input[i]=='i') || (input[i]=='o') || input[i]=='y' || input[i]=='u' )
{
input.erase(i,i+1);
}
input[i]+=dot;
}
cout<<input<<endl;
return 0;
}
From the cpluplus.com reference ( http://www.cplusplus.com/reference/string/string/insert/ )
// inserting into a string
#include <iostream>
#include <string>
using namespace std;
int main ()
{
string str="to be question";
string str2="the ";
string str3="or not to be";
string::iterator it;
// used in the same order as described above:
str.insert(6,str2); // to be (the )question
str.insert(6,str3,3,4); // to be (not )the question
str.insert(10,"that is cool",8); // to be not (that is )the question
str.insert(10,"to be "); // to be not (to be )that is the question
str.insert(15,1,':'); // to be not to be(:) that is the question
it = str.insert(str.begin()+5,','); // to be(,) not to be: that is the question
str.insert (str.end(),3,'.'); // to be, not to be: that is the question(...)
str.insert (it+2,str3.begin(),str3.begin()+3); // (or )
cout << str << endl;
return 0;
}
Also, check these links:
http://www.cplusplus.com/reference/string/string/
http://www.cplusplus.com/reference/string/string/append/
http://www.cplusplus.com/reference/string/string/push_back/
Before you try writing the code, you should write a detailed
specification of what it is supposed to do. With your code, I
can only guess: convert to lower case (naïvely, pretending that
you'll only encounter the 26 unaccented letters in ASCII), then
delete all vowels (again, very naïvely, since determining
whether something is a vowel or not is non-trivial, even in
English—consider the y in yet and day), and finally
inserting a dot after each character. The most obvious way of
doing that would be something like:
std::string results;
for ( std::string::const_iterator current = input.begin(),
end = input.end();
current != end;
++ current ) {
static std::string const vowels( "aeiouAEIOU" );
if ( std::find( vowels.begin(), vowels.end(), *current )
!= vowels.end() ) {
results.push_back(
tolower( static_cast<unsigned char>( *current ) ) );
}
results.push_back( '.' );
}
But again, I'm just guessing as to what you are trying to do.
Another alternative would be to use std::transform on the
initial string to make it all lower case. If you're doing this
sort of thing regularly, you'll have a ToLower functional
object; otherwise, it's probably too much of a bother to write
one just to be able to use std::transform once.
I’m assuming you want this input:
Hello world!
To give you this output:
h.ll. w.rld!
Rather than trying to modify the string in place, you can simply produce a new string as you go:
#include <cctype>
#include <iostream>
#include <string>
using namespace std;
int main(int argc, char *argv[]) {
string input;
getline(cin, input);
string output;
const string vowels = "aeiouy";
for (int i = 0; i < input.size(); ++i) {
const char c = tolower(input[i]);
if (vowels.find(c) != string::npos) {
output += '.';
} else {
output += c;
}
}
cout << output << '\n';
return 0;
}
Notes:
<cctype> is for toupper().
<string.h> is deprecated; use <string>.
Read whole lines with getline(); istream::operator>>() reads words.
Use tolower(), toupper(), &c. for character transformations. c + 32 doesn’t describe your intent.
When you need comparisons, c >= 'A' && c <= 'Z' will work; you don't need to use ASCII codes.
Use const for things that will not change.
I'm not sure how this old question got bumped back onto the current list, but after reviewing the answers, it looks like all will miss the mark if the input is more than a single word. From your comments, it appears you want to remove all vowels and place a '.' before the character immediately prior to where the removal occurred. Thus your example "tour" becomes ".t.r".
Drawing from the other answers, and shamelessly removing 'y' as from the list of vowels, you can do something similar to:
#include <iostream>
#include <string>
int main()
{
std::string input;
if (!getline (std::cin, input)) {
return 1;
}
size_t i = 0;
for (; input[i]; i++)
{
switch (input[i])
{
case 'A': /* case fall-through intentional */
case 'E':
case 'I':
case 'O':
case 'U':
case 'a':
case 'e':
case 'i':
case 'o':
case 'u':
{
size_t pos = input.find_first_not_of("AEIOUaeiou", i+1);
if (pos == std::string::npos) {
pos = input.length();
}
input.erase(i, pos-i);
if (pos - i > 1) {
input.insert(i, 1, '.');
}
input.insert(i-1, 1, '.');
break;
}
}
}
std::cout << input << '\n';
}
Example Use/Output
Your example:
$ ./bin/vowels-rm-mark
tour
.t.r
A longer example:
$ ./bin/vowels-rm-mark
My dog has fleas and my cat has none.
My .dg .hs f.l.s. nd my .ct .hs .n.n.
Based on your comments, it sounds like you want something like this:
#include <iostream>
#include <string>
#include <algorithm>
int main(int argc, char *argv[])
{
std::string input;
std::cin >> input;
std::transform (input.begin(), input.end(), input.begin(), tolower);
size_t i = 0;
while (i < input.length())
{
switch (input[i])
{
case 'a':
case 'e':
case 'i':
case 'o':
case 'y':
case 'u':
{
size_t pos = input.find_first_not_of("aeioyu", i+1);
if (pos == std::string::npos)
pos = input.length();
input.erase(i, pos-i);
break;
}
default:
{
input.insert(i, 1, '.'); // or: input.insert(i, ".");
i += 2;
break;
}
}
}
std::cout << input << std::endl;
return 0;
}