I'm working on a midterm project for my coding class, and while I've gotten the majority of kinks worked out I'm struggling with comparing two string values and determining if they are equal or not. The strings in question are ANSWERKEYand studentAnswers. The former is a constant that the latter is compared to.
The code in question is as follows.
if (studentAnswers == ANSWERKEY)
{
percentScore = 100.0;
cout << "Score: " << percentScore << " % " << 'A' << endl;
}
else if (studentAnswers != ANSWERKEY)
{
int count = 0;
double answerCount = 0.0;
while (count < ANSWERKEY.length())
{
if (studentAnswers.substr(count, count+1) == ANSWERKEY.substr(count, count+1)
{
answerCount++;
count++;
}
else
{
cout << "Incorrect answer." << endl;
count++;
}
}
percentScore = ((answerCount) / (double)ANSWERKEY.length()) * 100.0;
cout << "Percent score is " << percentScore << "%" << endl;
}
The exact issue I'm facing is that I can't work out a better way to compare the strings. With the current method, the output is the following:
The intro to the code runs fine. Only when I get to checking the answers against the key, in this case "abcdefabcdefabcdefab", do I run into issues. Regardless of what characters are changed, the program marks roughly half of all characters as mismatching and drops the score down because of it.
I've thought of using a pair of arrays, but then I can't find a solution to setting up the array when some values of it are empty. If the student's answers are too short, e.g. only 15 characters long, I don't know how to compare the blank space, or even store it in the array.
Thank you for any help you can give.
First:
if (studentAnswers == ANSWERKEY)
{...}
else if (studentAnswers != ANSWERKEY)
{ ...}
looks like an overkill when comparing strings. And where is the else part ?
Second, this is risky. Read the IEE754 and articles about cancellation, or even SO:
double answerCount = 0.0;
...
answerCount++
Third:
You are checking character by character using substr. To me it feels like using a hammer to kill a bacteria.
studentAnswers.substr(count, count+1) == ANSWERKEY.substr(count, count+1)
Fourth:
What if studentAnswers is shorter than ANSWERKEY ?
Conclusion:
You need to clarify inputs/expected outputs and use the debugger to better understand what is happening during execution. Carefully check all your variables at each step fo your program.
Related
I just started learning to code, starting with C++ yesterday. I need it for a project I'm doing and I like to build generation tools as an "onboarding" process when I learn a new skills. So I thought I'd try building out a regex generation tool.
I googled, I binged, and I looked through the similar questions and only saw answers pertaining to Ruby, Perl, or JS. Frankly, I'm a bit surprised given the utility and prevalence of C++, not more people have tried this.
I don't know how to go about the task, as I'm not a professional or really knowledgeable about what I'm doing. I'm not sure how to ask such questions, either. Please bare with me while I explain my current thoughts.
I am currently toying around with generating strings using byte arrays (I find the C++ type system and casting is confusing at times). I wanted to see if there were any specific ranges of random values that produce strings with latin characters more than others. I get a lot of different values, and found a few ranges that looked like sweet spots, but I ultimately don't know what numbers correlate to what characters.
I wanted to establish a pattern, then set the rand() ranges to correlate with the projected total byte value of what the pattern should generate as a string, then go fishing. I understand that I have to account for upper bounds for characters. So the generated values would be something like:
//not implemented
int getBoundary(string expression){
srand(time(0));
int boundaries[2] = {0};
boundaries[0] = getCeilingValue(expression)
boundaries[1] = getFloorValue(expression)
return boundaries
}
practice.cpp
/*
Method actually producing the byte strings
*/
void practice::stuub(int boundaries[2]){
srand(time(0)); //seed
basic_string<char> byteArray = {}; //"byte array" instantiation
for (int i = 0; i < 1000; i += 1) {
if(i % 2 ==0){
byteArray.push_back(rand() % boundaries[0]);//ceiling
}else{
byteArray.push_back(rand() % boundaries[1]);//floor
}
}
std::string s(byteArray, sizeof(byteArray)); //convert to string
cout << s << "\n";
}
/*
just a copy pasta validation function that I don't know if I need yet
*/
bool isNumeric(string str) {
for (int i = 0; i < str.length(); i++)
if (isdigit(str[i]) == false)
return false; //when one non numeric value is found, return false
return true;
}
/*
current putzing around. It's just been real fun to play around with,
but I plan to replace the instantiation of values of the "mod" array with
the upper/lower bounds of the string projected values This currently takes
a value and just does random stuff to it on a fishing expedition to see
if I can find any patterns.
*/
void practice::randomStringGen() {
try {
srand(time(0));
int mod[2] = {0};
string choice;
while (choice != "q") {
cout << "\n enter an integer to generate random byte strings or press (q) to quit \n";
cin >> choice;
if(choice != "q") {// make sure its not quit, otherwise it still carries out the tasks
if (isNumeric(choice)) {//make sure its numeric
mod[0] = stoi(choice);
if(mod[0] > 0) {//make sure its not 0
mod[0] = int(pow(mod[0], mod[0]));//do some weirdo math
mod[1] = rand() % mod[0]+1; //get another weirdo number
cout << "\n random string start:\n";
stuub(mod);//generate random string
cout << "\n :random string end\n";
}else{//user entered invalid integer
cout << "\n you did not enter a valid integer. Enter numbers greater than 0";
}
}else{
cout << "\n " << choice << " is not an integer";
}
}
}
}catch(std::exception& e){
cout << e.what();
}
}
I hope that provides enough explanation of what I am trying to accomplish.
I'm not any sort of pro, and I have very little understanding of what I'm doing.
I picked this up yesterday as a absolute beginner.
Talk to me like I'm 5 if you can.
Also, any recommendations on how to improve and "discretize" what I'm currently doing would be much appreciated. I think the nested "ifs" look wonky, but that's just a gut instinct.
Thanks!
I'm currently working through the book C++ Primer (recommended on SO book list). An exercise was given that was essentially read through some strings, check if any strings were repeated twice in succession, if a string was repeated print which word and break out of the loop. If no word was repeated, print that. Here is my solution, I'm wondering a) if it's not a good solution and b) is my test condition for no repeated words ok? Because I had to add 1 to the variable to get it to work as expected. Here is my code:
#include <iostream>
#include <vector>
#include <string>
using namespace std;
int main() {
vector<string> words = {"Cow", "Cat", "Dog", "Dog", "Bird"};
string tempWord;
unsigned int i = 0;
while (i != words.size())
{
if (words[i] == tempWord)
{
cout << "Loop exited as the word " << tempWord << " was repeated.";
break;
}
else
{
tempWord = words[i];
}
// add 1 to i to test equality as i starts at 0
if (i + 1 == words.size())
cout << "No word was repeated.";
++i;
}
return 0;
}
The definition of "good solution" will somewhat depend on the requirements - the most important will always be "does it work" - but then there may be speed and memory requirements on top.
Yours seems to work (unless you have the first string being blank, in which case it'll break); so it's certainly not that bad.
The only suggestion I could make is that you could have a go at writing a version that doesn't keep a copy of one of the strings, because what if they're really really big / lots of them and copying them will be an expensive process?
I would move the test condition outside of the loop, as it seems unnecessary to perform it at every step. For readability I would add a bool:
string tempWord;
unsigned int i = 0;
bool exited = false;
while (i != words.size())
{
if (words[i] == tempWord)
{
cout << "Loop exited as the word " << tempWord << " was repeated.";
exited = true;
break;
}
else
{
tempWord = words[i];
}
++i;
}
// Doing the check afterwards instead
if (!exited)
{
cout << "No word was repeated.";
}
a) if it's not a good solution
For the input specified it is a good solution (it works). However, tempWord is not initialized, so the first time the loop runs it will test against an empty string. Because the input does not contain an empty string, it works. But if your input started with an empty string it would falsely find as repeating.
b) is my test condition for no repeated words ok? Because I had to add 1 to the variable to get it to work as expected.
Yes, and it is simply because the indexing of the array starts from zero, and you are testing it against the count of items in the array. So for example an array with count of 1 will have only one element which will be indexed as zero. So you were right to add 1 to i.
As an answer for the training task your code (after some fixes suggested in other answers) look good. However, if this was a real world problem (and therefore it didn't contain strange restrictions like "use a for loop and break"), then its writer should also consider ways of improving readability.
Usage of default STL algorithm is almost always better than reinventing the wheel, so I would write this code as follows:
auto equal = std::find_adjacent(words.begin(), words.end());
if (equal == words.end())
{
cout << "No word was repeated" << endl;
}
else
{
cout << "Word " << *equal << " was repeated" << endl;
}
friends. I have a problem.
Problem: the computer must pick randomly one string out of an array of 36 strings. If by any chance it picks strings #34 or #35 (the two last ones), it has to draw two more random strings from the same array. I tried a do-while solution, and it "almost" works (see code below).
The randomization works fine - called srand inside main(). There is a forced "x2" draw (for testing reasons), so the computer draws two more strings. These two new random picks are NOT "x2", but still the loop kicks again - but just one more time! This time the computer picks two more "chits", which aren't "x2" either, so, as expected, it returns the "The chits have been drawn" sentence and the function is terminated. Why is the same code running twice with the same results but different if/else behavior? Thank you very much in advance.
string mortalityCalc ()
{
string mortalityChits[36] = {"1","2","3","4","5","6","7","8","9","10","11","12","13","14","15","16","17","18","19","20","21","22","23","24","25","26","27","28","29","30","-","-","-","-","x2","x2"};
int mortalityResult;
// mortalityResult = rand() %36;
mortalityResult = 35; // for testing only. Delete afterwards.
string drawnChit = mortalityChits[mortalityResult];
string drawnChit1;
string drawnChit2;
if (drawnChit != "-" && drawnChit != "x2")
{
string returnText = string("The computer has drawn the chit '") + drawnChit + "'.";
return returnText;
}
else if (drawnChit == "-")
{
string returnText = string("The computer has drawn the chit '") + drawnChit + "'. No senators died this year.";
return returnText;
}
do
{
cout << "The computer has drawn the 'x2' chit." << endl;
cout << "Two more chits will be drawn.\n" << endl;
mortalityResult = rand() %36;
drawnChit1 = mortalityChits[mortalityResult];
cout << "The first draw is the chit '" << drawnChit1 << "'. ";
mortalityResult = rand() %36;
drawnChit2 = mortalityChits[mortalityResult];
cout << "The second draw is the chit '" << drawnChit2 << "'." << endl;
} while (drawnChit1 == "x2" || drawnChit2 == "x2");
return "The mortality chits have been drawn. The corresponding senators are dead.";
}
UPDATE: Tried running this code isolated from the rest of the program and it behave as expected. So I guess it's important to post what comes before it:
cout << "If you are a lazy bastard, the computer can pick one senator randomly for you.\nAre you a lazy bastard? [y/n]" << endl;
string lazyBastard;
cin >> lazyBastard;
cout << endl;
if (lazyBastard == "y" || lazyBastard == "Y" || lazyBastard == "yes" || lazyBastard == "YES" || lazyBastard == "Yes")
{
mortalityCalc ();
cout << mortalityCalc () << endl;
cout << "Very well. Now, imminent wars become active (Only one of each match)." << endl;
cout << "Get ready for the next phase." << endl;
My guess, from reading some other questions here, is that somehow the cin is messing with the loop behavior, even though they are not related and there's no user input whatsoever in the loop's statements or conditions. Is that possible? If so, why and how to remedy it?
Thank you again.
In the first loop you are forcing an 'x2' so your are entering the do-while loop. The result of the two calls for 'rand())%36' is always 19 and a number between 30 and 34. The point is that the random number generator generates always the same sequence of numbers, if you don't give him a seed 'srand(...)'.
do {
// ...
cout << rand()%36;
// ...
} while( /*...*/ )
See http://ideone.com/zl8ggH
You have to create random numbers and your code does what you expect.
Finally! I thought it would be a stupid thing! I just realized that I called the mortalityCalc() function twice! That's why it was looping twice!
Thanks to all who tried to help!
Hi I'm not sure if this is the right place to ask this question.
Anyway I have written this code to parse a molecule formula and split it into atoms and amount of each atoms.
For instance if I input "H2O" I will for the atom array get {"H", "O"} and in the amount array I will get {2, 1}. I haven't taken account for amount that is larger than 9, since I don't think there are molecule which can bind to something that is larger than 8.
Anyway I'm quite newbie, so I wonder if this piece of code can be made better?
string formula = "H2O";
int no, k = 0, a = 0;
string atom[10];
int amount[10];
bool flag = true;
stringstream ss(formula);
for(int i = 0; i < formula.size(); ++i)
{
no = atoi(&formula[i]);
if(no == 0 && (flag || islower(formula[i]) ) )
{
cout << "k = " << k << endl;
atom[k] += formula[i];
flag = false;
cout << "FOO1 " << atom[k] << endl;
amount[a] = 1;
}
else if(no != 0)
{
amount[a] = no;
cout << "FOO2 " << amount[a] << endl;
a++;
flag = true;
k++;
}
else
{
k++;
a++;
atom[k] = formula[i];
cout << "FOO3 " << atom[k] << endl;
amount[a] = 1;
flag = false;
}
cout << no << endl;
}
Have you considered an approach with regular expressions? Do you have access to Boost or TR1 regular expressions? An individual atom and its count can easily be represented as:
(after edits based on comments)
([A-Z][a-z]{0,2})([0-9]*)
Then you just need to repeatedly find this pattern in your input string and extract the different parts.
There are many potential improvements that could be made, of course. But as a newbie, I guess you only want the immediate ones. The first improvement is to change this from a program that has a hard coded formula to a program that reads a formula from the user. Then try testing yout program by inputting different formulae, and check that the output is correct.
What if you modified it to be like this algorithm? This would maybe be less code, but would definitely be more clear:
// while not at end of input
// gather an uppercase letter
// gather any lowercase letters
// gather any numbers
// set the element in your array
This could be implemented with 3 very simple loops inside of your main loop, and would make your intentions to future maintainers much more obvious.
This question is unlikely to help any future visitors; it is only relevant to a small geographic area, a specific moment in time, or an extraordinarily narrow situation that is not generally applicable to the worldwide audience of the internet. For help making this question more broadly applicable, visit the help center.
Closed 10 years ago.
With the assistance of others, I have redone the code from scratch due to them pointing out numerous errors and things that wouldn't work. Thus I have changed the code massively.
I have the program working other than two formatting settings that I can't figure out how to get to work.
I need to only print "DAILY SCOOP REPORT" once at the top of the output, but I've moved it around but due to the way the arrays are set up I don't know where to put it.
Here is my code:
#include <iostream>
include
include
include
include
include
using namespace std;
int main()
{
string flavor_input, Capitalize;
string flavors[] = { "Chocolate", "Vanilla", "Strawberry", "Mint", "Rocky Road", "Mocha" };
int scoop_count [6] = { 0, 0, 0, 0, 0, 0 }, scoops = 0, j, k;
bool valid_option;
cout << "Welcome to Frozen Tongue Ice Cream Shop\n"<<endl;
cout << "Flavors avaliable: "<<endl;
cout << "Chocolate "<<endl;
cout << "Valnilla "<<endl;
cout << "Strawberry "<<endl;
cout << "Mint "<<endl;
cout << "Rocky Road "<<endl;
cout << "Mocha \n"<<endl;
while(true) {
cout << "Please enter the flavor of icecream sold or 'STOP' to exit.\n"<<endl;
getline (cin, flavor_input); // getline causes rocky road to be accepted with a space between the words.
string::iterator it( flavor_input.begin()); //converting the first letter of input to uppercase if not already.
if (it != flavor_input.end())
flavor_input[0] = toupper((unsigned char)flavor_input[0]);
while(++it != flavor_input.end())
{
*it = tolower((unsigned char)*it);
}
if (flavor_input == "STOP" || flavor_input == "Stop")
break;
valid_option = false;
for(int i=0;i<6;i++) //Checks to see if input matches those of possible inputs.
if(!flavor_input.compare(flavors[i]))
{
valid_option = true;
break;
}
if(!valid_option)
{
cout << "Invalid Flavor. Try again.\n\n";
flavor_input.clear();
continue;
}
for(int i=0;i<6;i++)
{
if(!flavor_input.compare(flavors[i]))
{
cout << "Enter how many scoops were sold: ";
cin >> scoops;
cin.ignore();
scoop_count[i] += scoops;
scoops = 0;
cout << '\n';
break;
}
}
}
for(int i=0;i<6;i++)
{
if(!scoop_count[i])
continue;
else
{
cout << "\nDAILY SCOOP REPORT: "<<endl;
cout << setiosflags( ios::left )
<< setw( 11 ) << flavors[i]
<< resetiosflags( ios::left )
<< setw( 4 ) << scoop_count[i] << endl;
}
}
cin.get();
return 0;
}
Thanks again for all of the assistance. It is greatly appreciated.
Thanks to all the assistance and pointing me in the direction of what to study, I have the program completed other than one last part.
I figured out that why it wasn't working when I moved the "DAILY SCOOP REPORT" line around. I had renamed the file and when I compiled it, it was outputing the "last working configuration" kinda deal if that makes sense. So I created a new project (the .cpp file has to have a certain name for submission) and put the code in it. Now the line is printed only once.
In the code block below, I have it where it lowers casing for all other letters other than the first or so it seems to be doing. The reason I have the case coding the way I do is that the instructions want the flavor report to print out with first letter of each word cap and lower after that. I am going to look into how to cap the 2nd "R" in Rocky Road, but other than the ignore white-space I don't really know how. Do I need to parse the line?
Anyone to point me in the right direction would be appreciated.
I tried but it gives error that in the first if statement "syntax error : identifier 'flavor_input'".
//converting the first letter of input to uppercase if not already.
string::iterator it( flavor_input.begin());
if flavor_input = "rocky road"
(it != flavor_input.end())
flavor_input[6] = toupper((unsigned char)flavor_input[6]);
if (it != flavor_input.end())
flavor_input[0] = toupper((unsigned char)flavor_input[0]);
while(++it != flavor_input.end())
{
*it = tolower((unsigned char)*it);
}
switch doesn't work with strings.
You need to use the operator == to select the right choice like so:
string count = // ... something
if (count == "choc") {
}
else if (count == "van") {
}
else if (count == "str") {
} // .. etc
A few other things: make sure you spell string with a consistent case, all lower case and no upper case. String is something different than string.
Make sure you surround strings with double quotes "" and not single quotes ''. single quotes are for single characters like 'a' or 'b'. double quotes are for multiple characters strings like "abc" and "hello"
Having the word count as both the function name and an argument name is probably a bad idea and will not compile because the same name means two different things.
You can't return multiple values from a function. writing something like return a,b,c; doesn't mean what you probably want it to mean. the comma (,) operator allows several expressions to be evaluated in the same statement and the result is the value of the last expression so writing return 1,2,3; is exactly the same as writing return 3;
C++ cannot switch on a string. Replace your switch(count) {...} with if/else if statements. Additionally the proper format for a string is "string", not 'string' (single quotes designate a single character, like: 'a'). Also, ensure that you always use the correct casing for string objects (string as opposed to String, like you have as your return values)
Other than that, it would be helpful to see the compiler errors you are getting.
Another thing I noticed in your source: you will have to omit the semicolons (-cola?) at the end of the following lines:
cout << "Please enter the flavor of icecream sold or 'STOP' to exit.\n"<<endl
<< "Flavors avaliable:\n"<<endl
<< "chocolate\n"<<endl
<< "valnilla\n"<<endl
<< "strawberry\n"<<endl
<< "mint\n"<<endl
<< "rocky road\n"<<endl
<< "mocha\n"<<endl
<< "Enter flavor : ";
This is just a single huge expression. The same applies to
cout << "\nDAILY SCOOP REPORT: \n"<<endl
<< "chocolate :"<<chocolate<<"\n"<<endl
<< "vanilla :"<<vanilla<<"\n"<<endl
<< "strawberry :"<<strawberry<<"\n"<<endl
<< "mint :"<<mint<<"\n"<<endl
<< "rocky road :"<<rocky_road<<"\n"<<endl
<< "mocha :"<<mocha<<"\n"<<endl;
Also: the endl and the "\n" are redundant. You will see the choices being separated by empty lines.
I haven't looked at the whole thing, but this isn't going to do what you want:
if (flavor = 'STOP' || 'stop')
I think you need:
if (flavor.compare("STOP") == 0 || flavor.compare("stop") == 0)
Let's go down the problems I see.
String count (string& flavor, string& count, string& chocolate, string& vanilla, string& strawberry, string& mint, string& rocky_road, string& mocha);
You're using String here, I'm sure you meant std::string or just string.
Inside the count function (SO is truncating the code when pasted), you're ending the line with a semicolon after endl yet trying to continue the stream output. I think you meant
Next:
if (flavor = 'STOP' || 'stop')
I think you meant to use the operator== instead of operator=, which is assignment not comparison. Also, there are no junctions in c++, so you will have to write that out as:
if (flavor == 'STOP' || flavor == 'stop')
Next:
switch (count) { case 'choc' :
Two problems here. First, you can only use plain-old-data (pod) in switch statements. Using std::string in a switch will not automatically call operator==; you will have to use if/else statements. Also, string literals are double quoted whereas character literals are single quoted.
Next:
chocCount + count;
This isn't really a statement. I'm sure you meant chocCount += count;
Next:
if (flavor = chocolate) chocCount + count;
Again, you want to use == and chocCount += count;.
Most of these problems are repeated. You should fix each of these problems everywhere they exist. There may be other problems, but I was basically compiling that in my head.
I didn't read through all of it to find semantic problems, but your count function is clearly not returning a count (at least what I currently see posted). You are returning a String, which I assume you meant string.
That's all this human compiler is going to solve for 1 homework assignment. I could recommend you go read a good C++ tutorial.