0

I'm trying to debug this program I wrote. How can I tell if, for a given word, hand, and word_list, it returns True or False? I tried initializing a variable failure and then modifying it and printing it's value. It isn't printing, so I don't know if it is behaving like it's supposed to. Any help is appreciated.

I have a function load_words() that returns a list of words. I know word is in word_list (I checked), so just trying to see if word is composed entirely of letters from the keys in the dictionary hand, which in this case it isn't, so it should return False.

Also, what is the difference between .keys() and .iterrkeys(), and is there a better way of looping through hand, perhaps with letter, value in hand.iteritems()?

word = 'axel'

hand2 = {'b':1, 'x':2, 'l':3, 'e':1}

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
    """
    failure = False
    if word in word_list:
        print hand
        print [list(i) for i in word.split('\n')][0]
        for letter in [list(i) for i in word.split('\n')][0]:
            print letter
            if letter in hand.keys():
                print letter
                return True
                failure = True
                print failure
            else:
                return False
                failure = False
                print failure
    else:
        return False
        failure = False
    print failure

is_valid_word(word,hand2,load_words())

UPDATE I wish to use this function in my function, but it gives a key error, even though it works fine on its own.

def update_hand(hand, word):
    """
    Assumes that 'hand' has all the letters in word.
    In other words, this assumes that however many times
    a letter appears in 'word', 'hand' has at least as
    many of that letter in it. 

    Updates the hand: uses up the letters in the given word
    and returns the new hand, without those letters in it.

    Has no side effects: does not modify hand.

    word: string
    hand: dictionary (string -> int)    
    returns: dictionary (string -> int)
    """
    for letter in [list(i) for i in word.split('\n')][0]:
        if letter in hand.keys():
            hand[letter] = hand[letter]-1
        if hand[letter] <= 0:
            del hand[letter]
    display_hand(hand)
    return hand
sampy
  • 305
  • 3
  • 4
  • 10

3 Answers3

4

The reason why it is not printing out is because you are returning the function before it prints. This means that the program stops before it reaches the print statement. For example:

def foo(x):
    return x
    print x

foo("asdf")

Will return nothing while:

def foo(x):
    print x
    return x

foo("asdf")

Will print:

asdf

So, all your statements before return. If not, it will not execute.

For your second clarification, this post already has your answer https://stackoverflow.com/a/3617008:

In Python 2, iter(d.keys()) and d.iterkeys() are not quite equivalent, although they will behave the same. In the first, keys() will return a copy of the dictionary's list of keys and iter will then return an iterator object over this list, with the second a copy of the full list of keys is never built.

Note that Python 3 does not have .iterkeys() too. Python 3 uses the previous .iterkeys() as the new .keys().

Lastly, I will review what is generally wrong with your code and what you want to achieve in descending order of severity.

  1. Your code only checks one letter
  2. [list(i) for i in word.split('\n')][0] is not how you get all the letters from a word.
  3. You should make short code return first so that you would not have big indent blocks.

Your code only checks one letter

In your for loop, you return True immediately after the first word is checked. You should return True after the loop is completed instead.

for letter in word:
    if letter not in hand.keys():
        return False
return True

List comprehension

Your list comprehension is not needed (I'll tell you why later) and need not be so complex just to get the letters from a word. E.g.

[list(i) for i in word.split('\n')][0]

Actually only does this:

list(word)

In fact, you should just iterate through the word directly (as I did above), it will return the letters one by one:

for letter in word:
    # code...

Make short code return first

Usually I dislike big chunks of highly indented code. What you can do is make the short code return first. For example:

if word in word_list:
    for letter in word:
        if letter in hand.keys():
            return True
        else:
            return False
else:
    return False

Can be simply be written as:

if word not in word_list:
    return False

for letter in word:
    if letter in hand.keys():
        return True
    else:
        return False

However, this is just my opinion. Some others may prefer the else statement so that they know when the code is executed.

Your final code would look like:

def is_valid_word(word, hand, word_list):
    if word not in word_list:
        return False

    for letter in word:
        if letter not in hand.keys():
            return False
    return True

Clean right? However, I assume that you are making something like a scrabble game, so you would count if the words in your hand can for the word you chose. What you can add is something to count if the number of letters in the word is less than or equal to the number of letters in your hand:

def is_valid_word(word, hand, word_list):
    if word not in word_list:
        return False
    # This makes the word into a "unique list"
    letters = set(word)
    for letter in letters:
        if hand[letter] < word.count(letter):
            return False

    return True

EDIT There was a problem with the code. It does not check if the letter is in hand in the if statement: if hand[letter] < word.count(letter):.

def is_valid_word(word, hand, word_list):
    if word not in word_list and word not in hand.keys():
        return False
    letters = set(word)
    for letter in letters:
        # Add this extra clause
        if letter in hand.keys() or hand[letter] < word.count(letter):
            return False

    return True
Community
  • 1
  • 1
Moon Cheesez
  • 2,489
  • 3
  • 24
  • 38
  • @sampy See my recent edit as I answered all your queries. If that's all, you can accept my answer. Thanks! – Moon Cheesez Jun 26 '16 at 09:39
  • this is excellent. I was wondering why I was only printing one letter, and it is so clear now. Yes I am making a scrabble game, and this function is to make sure the word is valid, of course. I tried adding a function I already made called *update_hand* which removes each occurrence of a letter in a word from the hand. However, I get a key error when I try to use it in my function, though it works fine on it's own. Your function also raises a key error, though only when I run a test program on it. I will edit my question to include it, perhaps you could provide some insight. – sampy Jun 27 '16 at 00:34
  • @sampy Yep, just found the problem. See my recent edit. – Moon Cheesez Jun 27 '16 at 02:03
  • I replaced hand[letter] with hand.get(letter,None), and it works perfectly. I modified my own function (replaced the clean list comprehension with your suggestion), and it seems to work fine, except with the unit test function I have. No big deal, I keep coming up with the same key error in that case. Yours works great, I'm just trying to understand this the best I can. Thanks for the help!!! – sampy Jun 27 '16 at 03:09
1

You can print the result directly print is_valid_word(word,hand2,load_words())

Juan Cortés
  • 20,634
  • 8
  • 68
  • 91
0

You have some indentation issues, and doing something after a return statement is futile.

You don't need to use keys or iterkeys the in operator will check for you, and will work with lists, set, dicts (keys), tuples, strings, ...

The in operator invokes __contains__ which is supported by most python collections.

Also have look at https://docs.python.org/2/reference/expressions.html#membership-test-details.

He is a minimized example of what you want to do with 3 tests.

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

    """

    if word not in word_list:
        return False

    for letter in word:
        if letter not in hand:
            return False

    return True

print(is_valid_word('bxel',
                    {'b': 1, 'x': 2, 'l': 3, 'e': 1},
                    ['foo', 'bar', 'bxel']))
print(is_valid_word('axel',
                    {'b': 1, 'x': 2, 'l': 3, 'e': 1},
                    ['foo', 'bar', 'axel']))
print(is_valid_word('axel',
                    {'a': 1, 'x': 2, 'l': 3, 'e': 1},
                    ['foo', 'bar', 'axel']))
totoro
  • 2,469
  • 2
  • 19
  • 23