0

Here is my code that solves a quadratic equation given user inputs a, b, c. However, I want to check for incorrect user inputs. If a user inputs anything but a float, the user is confronted with print "Not a valid input. Try again.\n" followed by a loop of the function quadratic2(). However, this program is part of a larger program, so I want the user to have the option of typing in "next" to terminate this function and move on. My issue is that I am calling for 3 user inputs, but I want the "next" input to be the first input, a. This works perfectly well until the user types in a random string, causing a ValueError. Once this happens, my code does not want to loop back to check for if the input is next. Please help me! I think something about the way this code is nested is messing me up.

def retest3():
    print "Type in another a, b, and c. Or type \"Next\" to move on."
def quadratic1():
    print ("This program calculates the zeroes of a quadratic equation."
    "\nPlease enter a, b, and c, hitting \"Enter\" after each one: ")
def quadratic2():
    while 1 == 1:
        a = (raw_input())
        if "next" == a.lower():
            ontonextthing()
            return 1 == 0
        else:
            try:
                a = float(a)
            except ValueError:
                print "Not a valid input. Try again.\n"
                quadratic2()
            try:
                b = float(raw_input())
            except ValueError:
                print "Not a valid input. Try again.\n"
                quadratic2()
            try:
                c = float(raw_input())
            except ValueError:
                print "Not a valid input. Try again.\n"
                quadratic2()

            if b**2-4*a*c>0:
                root1=(-b+math.sqrt(b**2-4*a*c))/(2*a)
                root2=(-b-math.sqrt(b**2-4*a*c))/(2*a)
                print ("First Root: {0}\nSecond Root: {1}".format(root1,root2))
            else:
                print ("The discriminant is negative, so your"
                " roots will contain \"i\".")
                disc1=(b**2-4*a*c)
                disc2=-disc1
                sqrtdisc2=(math.sqrt(disc2))/(2*a)
                b2=(-b)/(2*a)
                print ("{0} + {1}i".format(b2, sqrtdisc2))
                print ("{0} - {1}i\n".format(b2, sqrtdisc2))
        retest3()   

quadratic1()
quadratic2()
theo1010
  • 127
  • 10
  • This seems overly messy. Have you looked into `argparse`? It's quite nice https://docs.python.org/2.7/library/argparse.html – grill May 20 '15 at 04:52
  • [This post](http://stackoverflow.com/questions/2597104/break-the-nested-double-loop-in-python/4553525#4553525) shows a way to do with it with an exception if you must do this thing. – Abd Azrad May 20 '15 at 04:53
  • Thank you for your feedback. I did not check these suggestions because the answer below made it work just as I wanted to. I know it's very messy code, and I'm going to try to get into the habit of writing more neatly, but for my purposes with this little program, I've been working on it for awhile and am happy it works! – theo1010 May 20 '15 at 05:09

2 Answers2

3

This works perfectly well until the user types in a random string, causing a ValueError. Once this happens, my code does not want to loop back to check for if the input is next.

Seems like you want the continue statement:

        try:
            b = float(raw_input())
        except ValueError:
            print "Not a valid input. Try again.\n"
            continue

The continue statement takes you back to the beginning of your while loop (the next iteration). Currently you are calling quadratic2() which makes your function recursive, and not what you want.

Because it's recursive, when you receive your Exception it exits the current function, but because you were using recursion, you simply return back to the previous function you were in the middle of (which is the same function). Therefore the next input you type could be parsed by

b = float(raw_input())

instead of

a = (raw_input())
Martin Konecny
  • 57,827
  • 19
  • 139
  • 159
  • Thank you! This works now! Can you please explain to me a little more how `continue` works differently from re-calling the function? I think I somewhat get it... – theo1010 May 20 '15 at 05:06
  • 1
    `continue` tells the program to simply stop the current iteration of the while loop, and start the next iteration. Whereas with re-calling the function, it might be simpler to see what you're doing wrong by simply duplicating the entire `def quadratic2():` to another location call it `quadratic3()`, and make all calls from within `quadratic2` to `quadratic3` instead. Now do you see that if you exit `quadratic3` you return back to the middle of `quadratic2`? – Martin Konecny May 20 '15 at 05:09
  • AH there it is, thank you I get it now! Martin Konecny you are a lifesaver – theo1010 May 20 '15 at 05:11
  • Also, is there a faster and neater way of checking the 3 inputs for that certain string `next`? Just for future reference to avoid such messy code – theo1010 May 20 '15 at 05:15
  • you could put `raw_input` into a for-loop, to avoid duplication of code, but I think using it 3x is acceptable. – Martin Konecny May 20 '15 at 05:19
  • Sorry, I've hardly ever used for-loops, how would I set this one up? `for raw_input in a, b, c`? – theo1010 May 20 '15 at 05:21
  • 1
    A list comprehension with a for loop would work: `[get_input() for i in range(3)]` and then `def get_input(): float(raw_input())`. Make sure you put your exception handling in `def input` as well – Martin Konecny May 20 '15 at 05:26
  • 1
    However, because your first `raw_input` has different logic than the 2nd and 3rd, I wouldn't really recommend this approach. – Martin Konecny May 20 '15 at 05:27
1

The "real" solution for your problem is not to use deeply nested constructs in the first place. Deeply nested statements make your code hard to read, hard to test, hard to maintain and hard to reuse. Also, you will tend to duplicate code, which is bad. Especially in Python, deep nesting will cause you to count spaces to get the indentation right, which is really a pain in the neck.

Break your code into functions, and follow the "single responsibility principle" by letting one function do exactly one thing. This has several advantages:

  1. You don't have to keep too many things in your mind. Focus on one small problem, and be done with it.
  2. You can always return early in a function, so you can avoid nesting in many cases.
  3. You can reuse the code in your function.
  4. You don't have duplicate code: If you want to change a particular behavior, change one function instead of several places in one function.
  5. You can test your functions in isolation, making sure that they do the right thing.

In your case, the quadratic2 function does a lot of things:

  • It runs a "main loop"
  • It reads user input for commands
  • It dispatches the command
  • It reads user input for values
  • It handles invalid user input
  • It does computations
  • It displays the result back to the user

Now I don't say that you need one function for every detail listed above, but it is rather obvious that this function does too much.

Examples on how you could break it up:

  • Make one function for the main loop and for dispatching commands. Let the commands (such as "next") be handled in separate functions.
  • Write one function to read in a float from the user, and call this three times instead of repeating the try...except code three times. This has the added benefit that you can enhance this function to run in a loop, so that the user is asked to repeat the input for one value, instead of having to start all over again as your current solution does.

Other tips:

  • Don't use else after return, raise, break, or continue. It is not necessary, since the next statement will not be reached anyway, and you can save one nesting level.
  • Don't use confusing expressions like 1==1 or 1==0. This is just a complicated way of writing True and False. In your example, it should read while True and return False instead.
Ferdinand Beyer
  • 64,979
  • 15
  • 154
  • 145