
# a comment
# int
x = 10  
# float
y = 2.0
# string
s = "a string "
# tuple
t = (10, 20, "another string")

print t
print t[2]
# tuples are immutable

# list / array
l = [10, 20, "the string"]
print l[2]
# lists are mutable
l[2] = 30

# dictionary
d = {}
d['Chapel Hill'] = "pretty"
d['Detroit'] = "not so much"
d[t] = "a tuple in a dictionary"

# set
a = set()
a.add(1)
a.add('Chapel Hill')
a.add((4, 20))
a.add(1)

# simple function definition
def myFun(x):
    return x * x


f = myFun

print myFun(100)

# can redfine functions
myFun = 20

# higher-order function
# memoize wrapped function results
def memoize(f):
    memory = {}
    def with_mem(*args):
        if args in memory:
            return memory[args]
        else:
            memory[args] = f(*args)
            return memory[args]
    return with_mem

# decorator: modify function after definition
@memoize
def fib(n):
    if n == 0:
        return 0
    elif n == 1:
        return 1
    else:
        return fib(n - 2) + fib(n - 1)

@memoize
def my_test(x):
    print "i'm computing ", x
    return x + 1


