0

I am trying to break out of this loop. I have returned False and added a break if the character in the word is not on the hand, which is a dictionary with integer values per letter, based on Scrabble letter scores. This is part of a larger game but this particular function checks to see if the word I've entered is valid.

Now the issue I'm having is if the word entered is not in the word list the input is rejected and the player is asked to enter another word. HOWEVER...if I enter a single letter for instance, the hand is still updated to have that letter removed, and is therefore not available when I retype a word using all three letters.

For example:

if my hand is r u t

and I enter u, my word will be invalid, but the hand now contains only r t. Therefore rut will no longer be available.

I have a pretty good grasp of the loops and return statements after the last 24 hours straight of coding, except I can't figure out how to structure this loop to avoid that issue.

Here is the code:

def is_valid_word(word, hand, word_list):
"""
Returns True if word is in the word_list and is entirely
composed of letters in the hand. Otherwise, returns False.
Does not mutate hand or word_list.

word: string
hand: dictionary (string -> int)
word_list: list of lowercase strings
"""

    remaining = hand
    for c in word:
        if c not in remaining.keys():  """Loop 1"""
            return False
            break
        if c in remaining.keys():     """Loop 2"""
            remaining [c] -= 1
        if remaining [c] < 0:       """Loop 3"""
            return False
    if word in word_list:
        return True
    else:
        return False

However I structure the loops eventually a condition will fail. If I indent Loop 2 and 3 then they'll fail if the letter is not in the hand, and vice versa. Obviously a simple fix would be something like break(n loops) but that doesn't exist in Python. Any suggestions to make this happen? I've been working on it a long time as I'm new to programming.

Thanks in advance!

Chris
  • 89
  • 1
  • 2
  • 9
  • 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) – Łukasz Rogalski Aug 13 '16 at 19:34
  • I've looked at that question a number of times and didn't make a lot of sense to me, and the scenario seems a little different. I'm definitely trying to keep this code as absolutely simple as possible because I'm in a beginner python course and a simple answer is expected since we haven't learned too much yet. Also, I was hoping to get more of a personalized answer, which is why I put the time and effort into asking the question and placing the code. – Chris Aug 13 '16 at 19:39
  • I don't fully understand what you're asking for too clearly, but you could set a variable to true and loop while the variable is true, and then set it to false wherever you need to break out of the loop. – Harrison Aug 13 '16 at 19:49
  • I suggest that you use a debugger or `print()` statements to figure out what is going on in your code. – Code-Apprentice Aug 13 '16 at 19:51

1 Answers1

0

There are several problems in your code, I have refactored it to make it clearer (and fixed it, that was the point :))

minor issues:

  • not optimal: first check if word is in word_list (you'd better use a set rather than a list, would be much faster), then test for available letters
  • several return statements, followed by break. Confusing.

major issue:

The main problem being that you affect remaining to hand and you change it, thus destroying your head each time you call the function which explains the behaviour you're experiencing.

You have to copy the dictionary, and it will work. Python parameters are passed by reference, so if you pass a list or a dict and you modify it within the function, then it remains modified, unless you make a copy of it.

No real problems with the break & returns: my idea is that you think it does not work when you call your function several times in a row, when first call just destroys your input data.

def is_valid_word(word, hand, word_list):
    """
    Returns True if word is in the word_list and is entirely
    composed of letters in the hand. Otherwise, returns False.
    Does not mutate hand or word_list.

    word: string
    hand: dictionary (string -> int)
    word_list: list of lowercase strings
    """
    # make a copy or hand is destroyed by your test
    remaining = hand.copy()  
    rval = False

    if word in word_list:
        rval = True
        # don't check if word is not in word_list (optim)
        for c in word:
            if c not in remaining: # no need to specify keys()
                rval = False
                break

            remaining [c] -= 1
            if remaining [c] < 0:
                rval = False
                break

    return rval
Jean-François Fabre
  • 137,073
  • 23
  • 153
  • 219