Design

Saturday, November 13, 2010

Caesar Cipher in Python Using ASCII

ASCII is how American computers store numbers, letters, certain commands, and symbols as numbers. A binary byte is eight digits long, consisting of only 1 and 0. When ASCII was developed, there were 2^8, or 256 possible characters for 8-bit (1 byte) personal computers. Unicode is used more commonly and extensively, but ASCII is sufficient or this exercise. Consult an ASCII Table or play with python's default functions to determine the encoding of a character such as 'a', 'A' or '&'.

In python, we can find the ascii value using ord of a string
>>> ord('a')
97
>>> ord('A')
65
>>> ord('b')
98
>>> ord('1')
49
>>> ord('2')
50
>>> 3-0
3
>>> ord('3')-ord('0')
3

Notice how the lower case and upper case are unique. Also, the ASCII numeric value and the character increase by the same amount. The same trend is true for the continuation of the alphabet, if lower case or upper case characteristic is preserved. The string length must be 1 when using ord, because each index has its own ASCII value.For a multiple digit number or a word, you can use a for loop to go through the string.

The reverse of ord is chr, for character.
>>> chr(97)
'a'
>>> i=97
>>> while i<104:
... print chr(i)
... i+=1
...
a
b
c
d
e
f
g
>>> alpha=[]
>>> i=97
>>> while i<104:
... alpha.append(chr(i))
... i+=1
...
>>> print alpha
['a', 'b', 'c', 'd', 'e', 'f', 'g']


Caesar's shift was how Caesar communicated with his generals, in which each letter in the plaintext is replaced by a letter some fixed number of positions down the alphabet. For example, if an 'a' is encoded by a 'c' with a shift of 2, then a 'b' would be encoded by a 'd.' Reversely, an 'a' in the encoded text would be a 'y' in the plaintext. The following program was inspired by thepythonchallenge.com. My husband and I had a lot of fun testing and debugging by sending each other messages over email. The code only changes letters so that spaces, smilies, brackets, and other punctuations and characters will be preserved. string.maketrans would be very useful and less code for a one time application, but the following code is highly reusable.


#string will later allow us to test for ascii letters
import string

def encode(input,shift):
#create an empty string
foo = ''
#a for loop to shift the letters by the shift input
for x in input:
#changes only ascii letters
if x in string.ascii_letters:
#upper case end of alphabet, to loop back to A
if ord(x) > ord("Z") - shift and ord(x) <= ord("Z"):
new_ord = ord(x) + shift - 26
#lower case end of alphabet
elif ord(x) > ord("z") - shift and ord(x) <= ord("z"):
new_ord = ord(x) + shift - 26
else:
new_ord = ord(x) + shift
#other characters will remain unchanged
else:
new_ord = ord(x)

new_chr = chr(new_ord)
foo += new_chr

print "Your encoded message is ", foo

def decode(input,shift):
#create an empty string
foo = ''
#a for loop to shift the leters by the shift input
for x in input:
#changes only ascii letters
if x in string.ascii_letters:
#upper case start of alphabet, to loop back to Z
if ord(x) - shift < ord("A") and ord(x) >= ord("A"):
new_ord = ord(x) - shift + 26
#lower case end of alphabet
elif ord(x) - shift < ord("a") and ord(x) >= ord("a"):
new_ord = ord(x) - shift + 26
else:
new_ord = ord(x) - shift
#other characters will remain unchanged
else:
new_ord = ord(x)
new_chr = chr(new_ord)
foo += new_chr
print "Your decoded message is ", foo


def get_shift():
try:
shift = int(raw_input("How many characters do you want to\
shift?\n"))
except:
print 'Shift must be a number'
shift = get_shift()
if not (shift > 0 and shift <= 25):
print 'Shift must be between 1 and 25'
shift = get_shift()
return shift

def main():
try:
action=raw_input("Welcome to Caeser's Shifter! Would you \
like to encode or decode a message today?\n")
except:
print 'Please type encode or decode'
main()
shift = get_shift()

print "Ok, let's %s your message with a shift of %d." % (action, shift)
input=raw_input("What would you like to be translated?\n")

if action=='encode':
encode(input,shift)
elif action=='decode':
decode(input,shift)

if __name__ == "__main__":
main()


There are probably a few areas where redundancy can be reduced, but I am still learning as a developer. This was my first time trying exception handling, which I learned from my husband.

Tuesday, November 2, 2010

' \ ' Escapes in Python

I have previously mentioned that '\n' codes for a new line. The backslash is known in Python as an escape.
>>> print "\tThis is a tabbed line"
This is a tabbed line
>>> print "This sentence \nis split on \nthree lines"
This sentence
is split on
three lines
>>> print "Type \\ for a backslash"
Type \ for a backslash
>>> print "Verticle \vTab"
Verticle
Tab
>>> print "Shopping List: \vPrenatal vitamins \vCocoa Butter \
... \vOrange juice"
Shopping List:
Prenatal vitamins
Cocoa Butter
Orange juice
>>> print "Shopping List: \n\t*Prenatal vitamins \
...\n\t*Cocoa Butter \n\t*Orange juice"
Shopping List:
*Prenatal vitamins
*Cocoa Butter
*Orange juice
>>> print 'Prevent the single quote 5\'5" from ending the string'
Prevent the single quote 5'5" from ending the string
>>> print "Prevent the double quote 5'5\" from ending the string"
Prevent the double quote 5'5" from ending the string

Notice that I also used a backslash in my string so that I could continue onto the next ine without Python thinking my string was done. There are other escapes in Python you can read about. "Carriage return" is a term from type writers to mean that you move to the beginning of a line. Linefeed and formfeed are also typewriter terms. \n and \t will be the most frequently used, though you may also want to be familiar with other escapes for unique problems.

Monday, October 25, 2010

Guess the Number Game Python

With inspiration from InventWithPython.com, I wrote a game to guess a number between 1 and 20 with six attempts. This simple game is good practice for a beginner. My code is written in Python 2.6. The example from the link is written in Python 3 syntax.

import random

guessesTaken = 0

print 'Hello! What is your name?'
myName = raw_input() #get user name

number = random.randint(1,20) #random number between 1 and 20
print '%s I am thinking of a number between 1 and 20.' % myName

while guessesTaken < 6:
print 'Take a guess.'
guess = int(raw_input())
guessesTaken += 1
if guess < number:
print 'Your guess is too low.'
elif guess > number:
print 'Your guess is too high.'
else:
break #breaks out of loop

if guess == number:
if guessesTaken == 1:
print 'Good Job, %s! You guessed my number in 1 guess!'
else:
print 'Good Job, %s! You guessed my number in %d guesses!' % \
(myName,guessesTaken) #actual format on previous line without '\'


if guess != number:
print 'No. The number I was thinking of was %d' % number

Sunday, October 24, 2010

Python Debugger Article by Sontek

My husband wrote a wonderful blog post about debugging Python. Read it here.

String Formatting in Python

An easy way to combine strings with non strings is to use commas:
>>>>>> print 'The value of pi is often rounded to',3.14
The value of pi is often rounded to 3.14

But what if you want to create a website that welcomes guests by their name after he or she logs in? What if you are pulling information from a list or a dictionary? For personalization or multiple use functionality, we will be using string formatting. Below are the different format characters. There are a few others, but they aren't necessary.
%s   string
%d integers or decimals, but returns floor integer
%r anything that is not a string, converts object to a string
%f floating point, so we can control precision after decimal

Here is an example of using each of the formats:
>>> name='Anderson Silva'
>>> weight= 105 #integer
>>> height= 1.92 #decimal, but I only want 1.9 displayed
>>> home= 'Brazil' #string, watch what happens with %r
>>> fighter1='''%s
... Nationality: %r
... Weight: %d kg
... Height %.1f m''' % (name,home,weight,height) #in order
>>> print fighter1
Anderson Silva
Nationality: 'Brazil' #%r leaves quotes around strings
Weight: 105 kg
Height 1.9 m

Here is an example converting centimetres to inches
>>> def convert(cm):
... inches=cm*.39370
... print 'There are %.2f inches in %d centimetres' % (inches,cm)
...
>>> convert(10)
There are 3.94 inches in 10 centimetres
>>>#two places after decimal are shown
>>> convert(3.544445)
There are 1.40 inches in 3 centimetres

%f has a default to print six numbers after the decimal. By adding '.1', '.2', '.3', etc... between the '%' and 'f', you determine how many numbers are visible after the decimal. You can also add '+' or '-' before the '.x', such as showing a change
>>> print "Today's stock price changed %+.2f" % 1.4888
Today's stock price changed +1.49

Friday, October 22, 2010

Decimal Library for Changing Number Precision in Python

The built in math functions in Python use binary approximations, giving some funky results when dealing with numbers containing decimals:
>>> .1+.2
0.30000000000000004
>>> round(100.00/3.000,4)
33.333300000000001

One way to appropriately find the sum of decimals is to use strings
>>> str(.1+.2)
'0.3'

Also, the default is to round to the nearest whole number when dividing
>>> 1/3
0
>>> 100/3
33

The decimal library is a useful tool for floating point arithmetic. Instead of the command 'from decimal import *' that would import everything from decimal, all I need to import is Decimal and getcontext. When importing modules, simplicity is preferred. There are less problems with naming in your code and you can be more aware of the tools at your disposal. I already imported decimal and looked through the directory to determine which modules I wanted. I'm only going to show the precision feature of decimal. You may want to import the entire library if you want to use other functions.
>>> from decimal import getcontext
>>> from decimal import Decimal
>>> getcontext()
Context(prec=28, rounding=ROUND_HALF_UP, Emin=-999999999,
Emax=999999999, capitals=1, flags=[Inexact, Rounded],
traps=[DivisionByZero, Overflow, InvalidOperation])
>>> #our precision is also known as significant figures,
applied after arithmetic
... #let's change our precision
...
>>> getcontext().prec=6
>>> Decimal('1')/Decimal('7') #can be performed to strings
Decimal('0.142857')
>>> Decimal(1)/Decimal(7) #can be performed to integers
Decimal('0.142857')
>>> Decimal(10)/Decimal(7)
Decimal('1.42857') #notice that 6 is the total number of
figures, not the number after the decimal
>>> Decimal(10)/Decimal(5)
Decimal('2') #not '2.00000,' which is considered more
accurate than 2 by the science community

As someone with a science background, I found the decimal library's use of 'significant figures' interesting. Decimal can also be used in financial reporting or billing. You can also find maximums and minimums, change rounding properties, and do anything that you can do with the math library. I personally prefer the math library for the algebraic functions performed by decimal, because math's syntax is simpler. To learn more about decimal, click here .

Wednesday, October 20, 2010

Using Map Function in Python

Today I discovered the map function in Python. Map causes some simple for loops to be verbose and unnecessary. Let's look at how to change a list of integers to a list of strings. First we'll use a for loop:
>>> list=[1,2,3,4,5]
>>> index=0
>>> for x in list:
... list[index]=str(list[index])
... index+=1
...
>>> print list
['1', '2', '3', '4', '5']

Now we'll use the map function and we'll define our 1-5 list using the range function
>>> list1=range(1,6)
>>> map(str,list1) #performs str function to every index of list1
['1', '2', '3', '4', '5']

You can also make your list within the map function, such as splitting a string into a list. The following example shows how you can define a method using else, elif (else if), and if statements, then run your method to a single string with the map function, resulting in a list with the method performed to each index.
>>> def pluralize(word):
... if word[-1]=='y':
... return word[:len(word)-1]+'ies' #replaces the y with ies
... elif word[-1]=='s':
... return word+'es'
... else:
... return word+'s'
...
>>> map(pluralize, "The sexy waitress brought me a beer".split())
['Thes', 'sexies', 'waitresses', 'broughts', 'mes', 'as', 'beers']
>>>#my husband chose the sentence

I simplified my method to not account for every scenario put into it. Although this specific example does not produce correct English, the map function correctly split the string on the whitespace and carried out the method on each index of the list.