Design

Tuesday, May 15, 2012

Simple Dictionary Practice: Is Anagram?

I do not use dictionaries very often. Friday, I was without internet all day, so I took the opportunity to play with dir() and help() to discover some dictionary properties. My short-lived obsession with Draw Something on the iPhone has gotten me interested in anagrams (kicked the habit by reading programming books). I believe using dictionaries is the fastest and most accurate way to determine if two words are anagrams of each other.

A dictionary is an unordered set of key: value pairs. Keys must be an immutable type. Values can be anything. Review on mutable versus immutable here. Being unordered causes some interesting properties for working with dictionaries, different from any other python data structure. Instead of being indexed by numbers, dictionaries are indexed by keys. Because they are indexed by keys, each key is unique within it's dictionary. If two dictionaries with the same keys are added to each other, the values of the same data type combine. This is convenient for our anagram activity. But first, some dictionary review.


>>> sample_dict = {}        # creates an empty dictionary
>>> type( sample_dict )
<type 'dict'>
>>> sample_dict['Name'] = 'Jessie'        # creating a key:value pair
>>> sample_dict['Age'] = 23        # another key:value pair
>>> sample_dict
{'Age': 23, 'Name': 'Jessie'}
>>> sample_dict2 = {'Name': 'Jessie', 'Age': 23}        # another way to create dict
>>> type (sample_dict2)
<type 'dict'>
>>> sample_dict2
{'Age': 23, 'Name': 'Jessie'}
>>> sample_dict + sample_dict2        # cannot add dictionaries, only values
Traceback (most recent call last):
  File "<console>", line 1, in <module>
TypeError: unsupported operand type(s) for +: 'dict' and 'dict'
>>> sample_dict['Age'] + sample_dict2['Age']        # adds values
46
>>> sample_dict.keys()
['Age', 'Name']
>>> sample_dict.values()
[23, 'Jessie']
>>> type( sample_dict.values() )        # keys and values are returned as lists
<type 'list'>
>>> sample_dict.get('Age')        # gets the value at a specific key
23
>>> type( sample_dict.get('Age'))         # value maintains data type in dictionary
<type 'int'>
>>> sample_dict.has_key('Age')        # D.has_key() returns boolean
True
>>> sample_dict3 = {'Children': 'Graham'}
>>> sample_dict3.update(sample_dict)        # update keys and values
>>> sample_dict
{'Age': 23, 'Name': 'Jessie'}
>>> sample_dict3        # Children field is added as a key:value pair
{'Age': 23, 'Children': 'Graham', 'Name': 'Jessie'}
>>> {'Age': 23, 'Name': 'Jessie'} == {'Name': 'Jessie', 'Age': 23}        # different order is equal
True


How do we know if two words are anagrams? Consider the anagrams odor and door. We could say that they are reshuffled strings. Each word uses the same letters, but in a different order: 2 o's, 1 r, and 1 d. My simple program creates empty dictionaries for the two words being compared, stores the letters as keys, and adds to the value for each occurrence of the same letter, then checks that the dictionaries are equivalent. I have not included exception handling and I made the design decision to count white space as part of the anagram such that 'abc def' is not an anagram of 'fdeabc,' but is an anagram of 'abc fed.'


def get_dict(word, count):
     for i in word.lower():
         if count.has_key(i):
             count[i] += 1
         else:
             count[i] = 1
     return count

 def main():
     word1 = raw_input("What is the first word? \n")
     word2 = raw_input("What is the second? \n")
     count1 = {}
     count2 = {}
     count1 = get_dict(word1, count1)                              
     count2 = get_dict(word2, count2)
     if count1 == count2:
         print("Yes, those are anagrams!\n")
     else:
         print("No, you've failed \n")

 if __name__ == "__main__":
     main()


Friday, May 11, 2012

Book Review: The Practice of Programming

Affiliate Link

The Practice of Programming, written by Brian W. Kernighan and Rob Pike, was originally published in 1999. Although most programming books more than a couple years old are obsolete with out of date technology, The Practice of Programming is a pragmatic guide to become a better programmer regardless of your chosen language, framework, or supporting technologies. To quote the epilogue, "The world of computing changes all the time... but there are some constants, some points of stability, where lessons and insight from the past can help with the future." I highly recommend this book to any programmer who wants to establish good habits for writing understandable and consistent code. I also recommend this for programmers who want to or need to be able to work on projects with other developers. Although the exercises and examples were written in languages other than python, I was able to learn a lot. I reflected a lot on my own code, figured out new questions to ask about python, and am more appreciative that python does not suffer from some of the common pitfalls of other languages.

Monday, May 7, 2012

Mutable versus Immutable Objects

Strings, lists, tuples, integers, float, dictionaries, and sets are all types of python objects with different properties and uses. When using an object in a program or in your terminal, your session assigns an Id to access the computer's memory. The Id will be unique each session and on each computer, so the actual number returned by the id() function is irrelevant. What is relevant is when that number changes. If the id changes as the value changes, the computer had to assign a new id to the immutable object. If the id stays the same, then it is a mutable object, because the value associated with the id can be changed without changing the id. Integers are immutable:

>>> x = 5
>>> y = x    # direct the reference of y to be the reference of x
>>> id(x) == id(y)    # x and y point to same reference
True
>>> x = 4    # change the value of x
>>> id(x) == id(y)    # the id of x changed when we changed the value
False
>>> x == y    # the value of y did not change with x
False


I'm the type of learner that skims through vocabulary lessons to get to the action, but understanding this next part will save you some headaches when trying to manipulate mutable objects. Look what happens when I try to do the same thing I just did to the integers, but now to a list:

>>> x = [5]
>>> y = x    # direct the reference of y to be the reference of x
>>> id(x) == id(y)    # x and y point to the same reference
True
>>> x.append(4)    #change x from [5] to [5, 4]
>>> id(x) == id(y)    # x and y still point to the same reference
True
>>> x == y    # y changed with x
True
>>> y
[5, 4]
>>> z = [5, 5]    
>>> id(z)    # will be a different number for everyone
3075316972L
>>> z.append(3)    #z is now [5, 5, 3]
>>> id(z)    # id is constant, the list is mutable
3075316972L


I stumbled upon this while using random.shuffle on a list, while wishing to keep a copy of the list in it's original form. As you can see by assigning x equal to y, the lists changed together. That was an ineffective way to make a copy because all I did was assign the same Id two different names. Try determining if the other object types are mutable or immutable. I don't want to spoil the fun for you.