Skip to content

Instantly share code, notes, and snippets.

@BrianHicks
Last active August 29, 2015 13:55
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save BrianHicks/8774939 to your computer and use it in GitHub Desktop.
Save BrianHicks/8774939 to your computer and use it in GitHub Desktop.

Think Python

Chapters 4-6

Chapter 4

Case study: Turtle

4.2 (Flowers)

flowers

def petal(t, r, angle):
    for i in range(2):
        arc(t, r, angle)
        lt(t, 180 - angle)
        
def flower(t, n, r, angle):
    for i in range(n):
        petal(t, r, angle)
        lt(t, 360.0 / n)

flower(bob, 7, 60.0, 60.0)

flower.py

4.3 (pie)

pies

def isosceles(t, r, angle):
    y = r * math.sin(angle * math.pi / 180)

    rt(t, angle)
    fd(t, r)
    lt(t, 90+angle)
    fd(t, 2*y)
    lt(t, 90+angle)
    fd(t, r)
    lt(t, 180-angle)

def polypie(t, n, r):
    angle = 360.0 / n
    for i in range(n):
        isosceles(t, r, angle/2)
        lt(t, angle)
        
polypie(bob, 5, 40)

pie.py

4.4 (letters)

This one is big! Go look at the solution: letters.py

Chapter 5

Conditionals and recursion

Modulus Operator

Division:

quotient = 7 / 3
print quotient # 2

Modulus:

remainder = 7 % 3
print remainder # 1

Practical example! Modulus can be used to check even/odd numbers, like so:

some_number = 5
even = some_number % 2 == 0
print even # False

Logical Operators

and, or and not.

print True and False # False
print True or False  # True
print not True       # False

Use these to combine statements to create logic.

all_humans_are_mortal = True
socrates_is_a_human = is_a_human("socrates")

socrates_is_mortal = all_humans_are_mortal and socrates_is_a_human

Conditional Execution

Only run some code if a condition is met (IE True)

if all_humans_are_mortal and socrates_is_a_human:
    print "Yep, Socrates is mortal."

Alternative Execution

Something to run only if the condition is not True. Use else.

if all_humans_are_mortal and socrates_is_a_human:
    print "Yep, Socrates is mortal."
else:
    print "Socrates is either not mortal or not a human."

Chained Conditionals

Run a series of "if..else" in a nicer form with elif:

if favorite_color == "red":
    print "Red like a rose"
elif favorite_color == "yellow":
    print "Yellow as the sun"
else:
    print "Not red or yellow? Shame!"

Nested conditionals

You can put a conditional inside another conditional.

if all_humans_are_mortal:
    if socrates_is_a_human:
        print "Yep, Socrates is mortal."

In this case, it's equivalent to our example from earlier:

if all_humans_are_mortal and socrates_is_a_human:
    print "Yep, Socrates is mortal."

Recursion

It's legal for a function to call itself

def countdown(n):
    if n <= 0:
        print "Blastoff!"
    else:
        print n
        countdown(n - 1)

but not too much

def infinite():
    infinite()

Keyboard Input

Get input from the user (at the command prompt)

def echo():
    text = raw_input("Shout into the void: ")
    
    print "You hear back:"
    print text
    print text
    print text

ALWAYS use raw_input instead of input. Whatever you type into input is evaluated, and you want to control that logic.

5.3 (Fermat)

Fermat’s Last Theorem says that there are no integers a, b, and c such that a^n + b^n = c^n for any values of n greater than 2.

def check_fermat(a, b, c, n):
    if n > 2 and a ** n + b ** n == c ** n:
        print "Holy smokes, Fermat was wrong!"
    else:
        print "No, that doesn't work."

def check_fermat_interactively():
    a = int(raw_input("A: "))
    b = int(raw_input("B: "))
    c = int(raw_input("C: "))
    n = int(raw_input("N: "))
    
    check_fermat(a, b, c, n)

5.4 (Triangle)

If you are given three sticks, you may or may not be able to arrange them in a triangle.

def is_triangle(a, b, c):
    if a + b >= c or a + c >= b or b + c >= a:
        print "Yes"
    else:
        print "No"
        
def is_triangle_interactively():
    a = int(raw_input("side A: "))
    b = int(raw_input("side B: "))
    c = int(raw_input("side C: "))
    
    is_triangle(a, b, c)

Of course, this doesn't work for negative numbers

5.5 (Code examination)

What does this do?

def draw(t, length, n):
    if n == 0:
        return
    angle = 50
    fd(t, length*n)
    lt(t, angle)
    draw(t, length, n-1)
    rt(t, 2*angle)
    draw(t, length, n-1)
    lt(t, angle)
    bk(t, length*n)
    
draw(bob, 10, 5)

5.6 (Koch curve)

Koch curve

def koch(t, n):
    if n<3:
        fd(t, n)
        return
    m = n/3.0
    koch(t, m)
    lt(t, 60)
    koch(t, m)
    rt(t, 120)
    koch(t, m)
    lt(t, 60)
    koch(t, m)


def snowflake(t, n):
    """Draws a snowflake (a triangle with a Koch curve for each side)."""
    for i in range(3):
        koch(t, n)
        rt(t, 120)

Chapter 6

Fruitful Functions

Return Values

def square(n):
    return n * n

def cube(n):
    return n ** 3
    
a = square(2) # 4
b = cube(2)   # 8

Return values in Conditional Statements

def absolute_value(x):
    if x < 0:
        return -x
    else:
        return x

6.1 (compare)

def compare(x, y):
    if x > y:
        return 1
    elif x == y:
        return 0
    else: # x < y
        return 1

Boolean Functions

def is_divisible(a, b):
    if a % b == 0:
        return True
    else:
        return False

Since we're dealing with boolean values, this works better...

def is_divisible(a, b):
    return a % b == 0

Rule of thumb: any time you're returning a boolean inside a conditional, check whether you can return the result of the computation directly.

Checking Types

def factorial (n):
    if not isinstance(n, int):
        print 'Factorial is only defined for integers.'
        return None
    elif n < 0:
        print 'Factorial is not defined for negative integers.'
        return None
    elif n == 0:
        return 1
    else:
        return n * factorial(n-1)

leads to...

>>> factorial('fred')
Factorial is only defined for integers.
None
>>> factorial(-2)
Factorial is not defined for negative integers.
None

5.5 (Ackerman function)

Ackerman Function

def ackermann(m, n):
    if m == 0:
        return n+1
    if n == 0:
        return ackermann(m-1, 1)
    return ackermann(m-1, ackermann(m, n-1))

print ackermann(3, 4)

Technically, this is an Ackermann–Péter function.

5.6 (Palindrome)

def first(word):
    return word[0]

def last(word):
    return word[-1]

def middle(word):
    return word[1:-1]

def is_palindrome(word):
    if len(word) <= 1:
        return True
    if first(word) != last(word):
        return False
    return is_palindrome(middle(word))

is_palindrome("racecar") # True    

# 0: word = "racecar" (first and last are "r", recurse)
# 1: word = "aceca"   (first and last are "a", recurse)
# 2: word = "cec"     (first and last are "c", recurse)
# 3: word = "e"       (the length is 1, return True)

is_palindrome("ehorse") # False

# 0: word = "ehorse"  (first and last letters are "e", recurse)
# 1: word = "hors"    (first and last letters are not equal, return False)

Note that this breaks the rule of thumb discussed earlier, but for good reasons!

5.6 (Power)

A number, 'a', is a power of 'b' if it is divisible by 'b' and 'a/b' is a power of 'b'.

def is_power(a, b):
    print "Checking %d and %d" % (a, b)
    if a == b:
        return True
    elif a % b == 0:
        return is_power(a/b, b)
    else:
        return False

5.7 (Greatest Common Divisor)

From the book: "One way to find the GCD of two numbers is Euclid’s algorithm, which is based on the observation that if 'r' is the remainder when 'a' is divided by 'b', then 'gcd(a, b) = gcd(b, r)'. As a base case, we can consider 'gcd(a, 0) = a'."

def gcd(a, b):
    if b == 0:
        return a
    
    return gcd(b, a % b)
    
# 0: checking 55 and 10 (recurse to gcd(10, 5))
# 1: checking 10 and 5  (recurse to gcd(5,  0)) 
# 2: checking 5  and 0  (return 5)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment