Calling a function recursively for user input

2019-08-15 00:16发布

I'm trying to make a rock-paper-scissors game, and am trying to verify the input.

def player1():
    x = (raw_input("please select: Rock(r)/Paper(p)/Scissors(s): ")).lower()
    if x == 'r' or x == 'p' or x == 's' or x == 'rock' or x == 'paper' or x == 'scissors':
        return x[0]
    else:
        print "Error - wrong input!"
        player1() #I know I can run a While loop, but I need to run it this way.

print(player1())

If I enter the right input on the first try, everything works fine. But if I enter wrong input on first try, and enter the right input on the second time, I get None in the output, instead of the first letter of the RPS options.

What am I missing?

4条回答
三岁会撩人
2楼-- · 2019-08-15 00:43

In the else branch your function (the first call to your function, in particular) doesn’t return anything. In Python functions that do not return anything always return None implicitly.

查看更多
我想做一个坏孩纸
3楼-- · 2019-08-15 00:44

You're going to want to loop on input. What you're doing currently is recursively calling player1, and the recursive case doesn't have an explicit return value (hence, None is returned).

The way you do this is simple: while there is invalid input, prompt again. I'm using a modified version of this in the vein of the "while True break" style; it accomplishes the same goal. We loop indefinitely, and if the condition we want is valid, we return; otherwise, we prompt for input and loop again.

def player1():
    while True:
        x = raw_input("please select: Rock(r)/Paper(p)/Scissors(s): ").lower()
        if x == 'r' or x == 'p' or x == 's' or x == 'rock' or x == 'paper' or x == 'scissors':
            return x[0]
        else:
            print "Error - wrong input!"

As an alternative to that if statement, there is a slightly more clean way to express it via the in operator.

if x in ('r', 'p', 's', 'rock', 'paper', 'scissors'):

As an addendum to your original question (as it says that you have to do it recursively), I must strongly caution you against doing any input evaluation through recursion. Python has a call stack size of around 1,000, which means that you have a very finite (yet reasonably large) amount of tries before the program irrecoverably crashes.

Not just that, but your operation stack will be unnecessarily filled with method calls that behave in a similar fashion to a loop. For memory's sake in addition to the absolute recursion ceiling, do not use recursion for this.

If you absolutely must, and again I strongly advise against doing this, then you simply have to return from your iterative case.

def player1():
    x = (raw_input("please select: Rock(r)/Paper(p)/Scissors(s): ")).lower()
    if x == 'r' or x == 'p' or x == 's' or x == 'rock' or x == 'paper' or x == 'scissors':
        return x[0]
    else:
        print "Error - wrong input!"
        return player1() #I know I can run a While loop, but I need to run it this way.

print(player1())
查看更多
一夜七次
4楼-- · 2019-08-15 00:48

I'm adding this answer for completeness sake, as you do ask for a recursive solution. Here is my solution that is closest to yours:

def player1():
    x = (raw_input("please select: Rock(r)/Paper(p)/Scissors(s): ")).lower()
    if x == 'r' or x == 'p' or x == 's' or x == 'rock' or x == 'paper' or x == 'scissors':
        return x[0]
    else:
        print "Error - wrong input!"
        return player1()

As you can see, all you've forgotten is a return statement. A more readable way might be:

def player1():
    playerInput=None
    while playerInput not in ('r', 'p', 's', 'rock', 'paper', 'scissors'):
        if playerInput is not None:
            print("Error - wrong input!")
        playerInput = raw_input("please select: Rock(r)/Paper(p)/Scissors(s)").lower()
    return playerInput[0]

It would be even cleaner with a do while loop, but python lacks that construct.

查看更多
我欲成王,谁敢阻挡
5楼-- · 2019-08-15 00:51

An improved version of @Makoto's example:

def player1():
    x = raw_input("please select: Rock(r)/Paper(p)/Scissors(s): ").lower()
    while True:
        if x in ['r', 'p', 's', 'rock', 'paper', 'scissors']:
            return x[0]
        else:
            print "Error - wrong input!"
            x = raw_input("please select: Rock(r)/Paper(p)/Scissors(s): ").lower()

This is a little more concise than many or expressions which gets a bit unwieldy if you have a lot of conditions you want to check!

A little explanation:

We're checking to see x (our user input) is in pre-defined list of valid inputs.

An even more generalized version of this which becomes reusable:

Example: (reusable, non-recursion:)

#!/usr/bin/env python


from __future__ import print_function  # For Python 2/3 compat


try:
    input = raw_input  # For Python 2/3 compat
except NameError:
    pass


def prompt(prompt="Enter: ", valid=None):
    s = input(prompt)
    while valid and s not in valid:
        print("Invalid input! Please try again. Valid inputs are {0:s}".format(" ".join(valid)))
        s = input(prompt)
    return s


x = prompt("Enter action ([r]ock, [p]aper, [s]cissors): ", ["r", "p", "s", "rock", "paper", "scissors"])

Demo:

$ python foo.py
Enter action ([r]ock, [p]aper, [s]cissors): a
Invalid input! Please try again. Valid inputs are r p s rock paper scissors
Enter action ([r]ock, [p]aper, [s]cissors): foo
Invalid input! Please try again. Valid inputs are r p s rock paper scissors
Enter action ([r]ock, [p]aper, [s]cissors): r

$ python foo.py
Enter action ([r]ock, [p]aper, [s]cissors): a
Invalid input! Please try again. Valid inputs are r p s rock paper scissors
Enter action ([r]ock, [p]aper, [s]cissors): foo
Invalid input! Please try again. Valid inputs are r p s rock paper scissors
Enter action ([r]ock, [p]aper, [s]cissors): rock

PS: Sorry I didn't answer your question using recursion. IHMO this is not a good use-case for recursion. Oh well :) However; this is pretty easy to change:

Example: (reusable, recursive)

def userprompt(prompt="Enter: ", valid=None):
    s = input(prompt)
    while valid and s not in valid:
        print("Invalid input! Please try again. Valid inputs are {0:s}".format(" ".join(valid)))
        s = userprompt(prompt, valid)
    return s
查看更多
登录 后发表回答