0

I'm asking users a series of questions and recording their answers. My question-askers check the formatting of these answers and return a flag if a user inputs something strange (like my name is 102).

I'd like this program to break out of the question set if any of these answers are wrong, right away. I'm attempting to use a while loop to do this but it is clear to me that this while loop only checks the value of the flag at the end of each loop, so it won't restart the question asking process until the block of questions are done.

Note the `letter' variable is a surrogate for user input in this example. This is not how the code actually looks.

def string_checker(letter, output):
    if type(letter) != str:
        print('You did not give me a string!')
        output = 1

    return output

output = 0
# this should go until question 3, when the user makes a mistake
while output == 0:

    # question 1
    letter = 'bob'
    print(letter)
    output = string_checker(letter, output)

    # question 2
    letter = 'aldo'
    print(letter)
    output = string_checker(letter, output)

    # question 3 --- user gets this wrong
    letter = 1
    print(letter)
    output = string_checker(letter, output)

    # question 4
    letter = 'angry'
    print(letter)
    output = string_checker(letter, output)

# but it seems to ask question 4, regardless
print('done!')

Is there a way for me to modify this code so that question 4 is never asked?

UPDATED CODE BASED ON JASPER'S ANSWER

Building on Jasper's answer with a full solution... this modification to my problem solved it. By raising a ValueError inside of the checking function, the try block fails immediately and we can escape from main using a return.

def string_checker(letter):
    if type(letter) != str:
        raise ValueError

def main():
    # this should go until question 3, when the user makes a mistake
    try:

        # question 1
        letter = 'bob'
        print(letter)
        string_checker(letter)

        # question 2
        letter = 'aldo'
        print(letter)
        string_checker(letter)

        # question 3 --- user gets this wrong
        letter = 1
        print(letter)
        string_checker(letter)

        # question 4
        letter = 'angry'
        print(letter)
        string_checker(letter)

    # we make a mistake at question 3 and go straight to here
    except ValueError as ve:
        print('You did not give me a string!')
        return 'oops'

    # exit
    return 'done'
jdv
  • 13
  • 4
  • possible duplicate of [How to break out of multiple loops in Python?](http://stackoverflow.com/questions/189645/how-to-break-out-of-multiple-loops-in-python) – heinst Jul 22 '14 at 15:36
  • Surely you should be using a loop that iterates over the list of letters, something more like: `for letter in ['x', 'y', 1, 'z']: print(letter); x = string_checker(letter, x); if x != 0: break` (spread over multiple lines, indented properly, and without any semicolons). Seeing almost the same block of code repeated 4 times like that should give you the shivers — or make your nose twitch at the code smell. (There still may be better ways to code that, but it would be a simple step up.) – Jonathan Leffler Jul 22 '14 at 15:36
  • Yes, I do not like this repetition either. However for me to have all of the 'letters' in that list, I would have already had to ask the set of questions, so my user still isn't spared the wasted time if they make a mistake along the way. – jdv Jul 22 '14 at 15:40
  • Do you ever intend to actually loop over the questions? If not, you should throw an exception when a "wrong" answer is given. – Jasper Jul 22 '14 at 15:42
  • So it seems like while loops just don't do what I want them to at all. Raising exceptions doesn't work very nicely, as it breaks the flow of things and forces the user to start over. Imagine this is a small chunk of a 100 question set split into little modules, and if they make an error I just want them to have to repeat a single module. – jdv Jul 22 '14 at 15:49
  • Your function should probably do the `print` operation. I don't see how changing from single letters to strings invalidates my observations about using a list — but then I don't see the distinction between creating the list (possibly at runtime) and writing out the list longhand (definitively as you are coding); indeed, using the list opens the way to far greater flexibility. Indeed, it isn't clear where the strings come from; maybe you're hiding some user interaction from us, which makes it hard to assess. However, that's your problem, not mine. – Jonathan Leffler Jul 22 '14 at 15:58
  • Hi Jonathan, yes I am hiding a large amount of user interaction that goes far beyond the scope of this question. The letter variable being assigned is a surrogate for user input. If my code was actually working as I have shown it here, your list suggestion would undoubtedly be best. I'll make that clearer in my question. – jdv Jul 22 '14 at 16:04
  • Please do **not** insert answers inside a question. If you have an answer *post it as such* (it is okay to answer your own question, and even accept the answer if it is a good one). Also try to avoid adding useless tags in the title. Don't just add a random `python` at the beginning of the title. If you already have put that information in the tags that's enough. The title should be a *well-formed sentence*, without random tags in it. – Bakuriu Jul 22 '14 at 17:12
  • Why do you need to check for the *type* of the result? How are you asking the user for input? AFAIK you should *always* obtain a string from the user, then you may want to check whether the string is formatted in a certain way, or convert it to certain type. If you are using python2's `input` function be aware that you *shouldn't* use that since it opens huge security holes in your program. Also to check for a given type use `isinstance(something, str)` and not `type(something) == str`. Lastly if the length of the code depends on the number of question you have something wrong anyway. – Bakuriu Jul 22 '14 at 17:23
  • Hi Bakuriu, I'm asking the user for raw_input, converting that to the appropriate type, and then sometimes checking for ints, or strings, or checking whether the input int is an index in a list, or checking whether the input string is a key in a dict, or ... The point is I want the code to stop immediately after a mistake, give feedback, and start again. These questions are pretty specific to the programs this python code is externally accessing, so the length of the code does need to scale with the number of questions asked, but the number of questions asked is hard-coded. – jdv Jul 22 '14 at 18:05
  • Also, I'm adding my solution code back to my answer because it won't allow me to post an answer. Please don't remove it, I think it will help people in the future. – jdv Jul 22 '14 at 18:11

3 Answers3

4

You can check if the result of string_checker is 1 after each question:

if output == 1:
    break

The break statement will exit the loop immediately.

By doing this, you won't need to have that condition in the while, so you can do an infinite while:

while True:
    ...
jdv
  • 13
  • 4
Christian Tapia
  • 33,620
  • 7
  • 56
  • 73
  • This is a decent solution, but only exacerbates Jonathan Leffler's (and my own) concern of having a large amount of repeated code. – jdv Jul 22 '14 at 15:51
0
# question 3 --- user gets this wrong
letter = 1
print(letter)
output = string_checker(letter, output)

# Add this to your code:

if output == 1:
    break

The break jumps out of the while loop completely, and you're good to go. The only problem is, the computer will still

print('done!')

So maybe you want to include an error code or something of the like.

Maybe something like?

if output == 1:
    print "You have given an invalid input"
    break

EDIT:

I realized it would print "You have given an invalid input" and then print "Done!"

So instead, you should have the program stop running with the invalid input:

if output == 1:
    print "You have given an invalid input"
    return

The return statement will stop the program completely.

Mburdzy
  • 35
  • 4
0

While this does not answer the question title, I think a loop is the wrong design choice (assuming you are not going to ask the same questions multiple times). Instead, you could raise exceptions:

try:
  # ask question

  if not string_checker(...):
    raise ValueError

  # ask next question

except ValueError as ve:
  print("wrong answer")
Jasper
  • 3,939
  • 1
  • 18
  • 35
  • Thanks for the implementation, this works perfectly! I'll edit my answer with the solution. – jdv Jul 22 '14 at 15:55