简体   繁体   中英

Why this simple logic doesn't work?

I just couldn't figure out of why this simple logic in while loop doesn't work.

Basically what the function does is that, it takes an argument and will check in the while loop to see whether if it is not "yes" or "no" then keep looping.

void getAnswer(string answer) {

    string newAnswer = "";
    newAnswer = answer;
    while (newAnswer != "Yes" || newAnswer != "yes" || newAnswer != "Y" || newAnswer != "y" || newAnswer != "No" || newAnswer != "no") {
            cout << "Enter yes or no.." << endl;
            cin >> newAnswer;

        }

        if (newAnswer == "Yes" || newAnswer == "yes" || newAnswer == "Y" || newAnswer == "y") {
            chooseOptions();
        } else {
            cout << "Bye See you again " << endl;
            exit(1);
        } 
}

But even though I enter "yes" or "no" , it will still looping.

De Morgan's laws state that:

"not (A and B)" is the same as "(not A) or (not B)"

Applying this law on your on your condition:

newAnswer != "Yes" || newAnswer != "yes" || ...

!(newAnswer == "Yes" && newAnswer == "yes" && ...)

Now it's easier to see why it's wrong.

If newAnswer is something that is not "Yes" or "yes", it'll be evaluated to true, and that's not what you want.

The solution would be changing || to && .

Think about it. I ask you a yes or no question. You tell me "no".

I go down my list, and stop as SOON as I get to a true statement, because that's what logical OR means.

newAnswer != "Yes" >> true. Done. Continue looping.

Fine, you say. This time, you answer "Yes".

newAnswer != "Yes" >> false

newAnswer != "anything else" >> true. Done. Continue looping.

This is a tautology. It's not possible to make that OR condition false. You should use AND.

In your own words, it's not "while not yes or no" but "while not yes AND not no AND not n AND not y..."

Apart the evident inversion between && and || all answer talk about, let me go a little bit further, to make another fallacy evident (not really a bug, but a design weakness): you ask "yes or no" accept anything and if the answer looks-like "yes" you exit. Anything else plays like a no. Even "one cup of coffee".

And inverting all the logic has the same fallacy: check for N,n,No,no to decide to don't exit means exit if "tea and biscuits" are answered.

Also, getAnswer takes answer and calls chooseOptions (which in turn will get some input to give to getAnswer ): you're not looping : you are recoursing . And you are doing a same thing (get an answer from an input) from two different places. What does it happen if I want to change std::cin with some other stream? Everything to be changed? In how many different places?

A much better logic should force the answer to be consistent.

In your main program you should do most likely something like

bool do_exit = false;
while(!do_exit)
{
    //All useful stuff and then ...

    do_exit = getAnswer(std::cin, std::cout, 
      "Do you want to exit? [yes/no]", 
      "please answer yes or no");
}

Or, more concisely,

for(bool do_exit=false, !do_exit, do_exit=getAnswer(
      "Do you want to exit? [yes/no]", 
          "please answer yes or no") )
{
  //usefull stuff here
}

Now, lets get into the answer logic:

You have to get one line of input (not just one word: one line, since I can type "yes, I want"), so cin>>string doesn't play well: better std::get_line . If it is "yes" return true, if "no" return false and if anything else repeat the reading.

bool getAnswer(std::istream& istr, std::ostream& ostr, 
   const std::string& prompt, const std::string& reprompt)
{
  ostr << prompt << std::endl;
  for(;;)
  {
     std::string line;
     std::getline(istr,line);
     if(line == "yes" || line == "Yes" || line == "Y" || line == "y") 
        return true;
     if(line == "no" || line == "No" || line == "N" || line == "n") 
        return false;
     ostr << reprompt << std::end;
  }
}

This makes you accept well known "yes or no" variants, but refuse anything else.

Going even furtherly, we can cosider the idea that cin/cout can be other kind of streams, and may be there is no one "typing".

To avoid to go in an infinite loop, we can introduce an attempt limit and throw an exception if it is reached:

bool getAnswer(std::istream& istr, std::ostream& ostr, 
   const std::string& prompt, const std::string& reprompt)
{
  ostr << prompt << std::endl;
  for(int attempt=5; attempt>0; --attempt)
  {
     std::string line;
     std::getline(istr,line);
     if(line == "yes" || line == "Yes" || line == "Y" || line == "y") 
        return true;
     if(line == "no" || line == "No" || line == "N" || line == "n") 
        return false;
     if(attempt>1)
       ostr << reprompt << " ("<<attempt-1<<" attempts remaining)"<<std::endl;
  }
  throw std::domain_error("cannot get valid input");
}

and let the caller to eventually catch and do some other action, or eventually gracefully terminate.

Furthermore, we can parametrize not just the i/o and the questions, but also the answers, but this is getting too far.

The logic is as follows:

If your string input is a yes :

newAnswer != "yes"  --> false

newAnswer != "Yes" --> true
newAnswer != "Y" --> true
newAnswer != "y" --> true
newAnswer != "No" --> true 
newAnswer != "no" --> true

The logical or of several true s and one false is a true . This means that for an input of a yes input, the loop will continue. If you change all those or s to and s, the logical and of several true s and one false will be false . If the input is a yes input, the loop will not continue.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM