Day 4 Reading Journal Notes

Exercise 4

Write a function called middle that takes a list and returns a new list that contains all but the first and last elements. So middle([1,2,3,4]) should return [2,3].

The most common answer

def middle(s):
    return s[1:len(s)-1]

middle([1,2,3,4,])
[2, 3]

A more cumbersome method, but works?

def middle(lst):
    new = []
    for x in range(0, len(lst)):
        new.append(lst[x])
    new.remove(new[0])
    new.remove(new[-1])
    return new
print(middle([1,2,3,4]))
print(middle([1,5,3,4]))
[2, 3]
[5, 3]

Careful about return types: [[2, 3]] != [2,3]

def middle (random_list):
    new_list = []
    for element in random_list:
        new_list = [random_list[1:-1]]
    return new_list

middle([1,2,3,4])
[[2, 3]]

Improving readability

Variable names and a docstring.

def middle(first_list):
    """
    Takes a list and returns all but the first and the last elements.
    """
    last_index = len(first_list) - 1
    new_list = first_list[1:last_index] #this all could be one line but I like readability
    return new_list

my_list = [1,2,3,4]
print(middle(my_list))
print(my_list)
[2, 3]
[1, 2, 3, 4]

Is this one correct?

How can we write a unit test?

def middle(Input):
    """
    Given a list, return a new list that contains all but the first and last elements.
    
    >>> L = [1, 2, 3, 4, 5]
    >>> print(L)
    [1, 2, 3, 4, 5]
    >>> print(middle(L))
    [2, 3, 4]
    >>> print(L)
    [1, 2, 3, 4, 5]
    """
    if len(Input)>1:

        List = Input
        del List[0]
        del List[-1]
    
        return List
    else:
        
        return "List is too short."

import doctest
doctest.run_docstring_examples(middle, globals(), verbose=False)

#middle(['a', 'b'])
#middle(['a'])

Quick fix: make a copy of the input list using a slice: List = Input[:]

Using the copy module

Slices are another way to go, but this will work.

original_list = [1, 2, 3, 4, 5]

import copy
def middle(original_list):
    copied_list = []
    copied_list = copy.copy(original_list)
    del copied_list[0]
    del copied_list[-1]
    
    print(original_list)
    print(copied_list)
    return copied_list
    
middle(original_list)
[1, 2, 3, 4, 5]
[2, 3, 4]





[2, 3, 4]

Another correct solution, this one using pop

def middle(l):
    new_list = l[:]
    new_list.pop(0)
    new_list.pop(len(new_list)-1)
    print(l)
    return new_list

middle([1,2,'ads',2,35,6,63])
[1, 2, 'ads', 2, 35, 6, 63]





[2, 'ads', 2, 35, 6]

Careful with Variable Names

def middle(list):
    new_list = list[1:len(list)-1]
    return new_list
print(middle([9,2,7,1,4]))
[2, 7, 1]

Another way to make a copy of a List???

def middle(L):
    res = list(L)
    res.pop(0)
    res.pop(-1)
    return res

print(middle([1,2,3,4]))
print(middle([2,"hello",5,'Hi']))

Exercise 5

Write a function called chop that takes a list, modifies it by removing the first and last elements, and returns None.

What is the difference between middle and chop? Sketch out the program state or take a look at each in Python Tutor.

This looks okay, but we haven’t really tested it yet

How can we see whether or not this is correct?

def chop(lst):
    lst = lst[1:len(lst)-1]
L = [1, 2, 3, 4]
chop(L)
print(L)
[1, 2, 3, 4]

To better understand what is happening here we will draw some state diagrams and stack diagrams.

This seems better, is it correct?

def chop(list):
    list.remove(list[0])
    list.remove(list[-1])
    return None
middle([1,2,3,4])

How about this one?

We have a minor quibble over variable names.

def chop(list_numbers):
    del list_numbers[0]
    del list_numbers[-1]
    print(list_numbers)
    
chop([1,2,3,4])
[2, 3]

del can also handle list slices

X = [3,6,3,8,0,2,5]
def chop(List):
    del List[:1]
    del List[-1:]
    
print(chop(X))
print(X)
None
[6, 3, 8, 0, 2]

Exercise 7

Two words are anagrams if you can rearrange the letters from one to spell the other. Write a function called is_anagram that takes two strings and returns True if they are anagrams.

Let’s decide if each of these is correct

Minor issue with doctest here. Let’s run it with verbose = True

def is_anagram(string_one, string_two):
    """
    Takes input two strings and returns True is they are anagrams
    
    >>> is_anagram("car", "arc")
    True
    >>> is_anagram("blah", "nope")
    False
    """
    if len(string_one) == len(string_two):  # words are the same length
        for letter in string_one:
            if letter in string_two:
                return True
        return False

import doctest
doctest.run_docstring_examples(is_anagram, globals(), verbose=False)

Passes tests, but we don’t have enough coverage. For example, what if we add:

    >>> is_anagram("cat", "cap")
    False
    >>> is_anagram("ab", "aaaab")
    False
def is_anagram(o,t):
    """
    >>> is_anagram("ab", "aaaab")
    False
    """
    if o.strip(t) == '':
        return True
    else:
        return False

is_anagram('banana','popcorn')
is_anagram('banana','nabana')

import doctest
doctest.run_docstring_examples(is_anagram, globals(), verbose=True)
Finding tests in NoName
Trying:
    is_anagram("ab", "aaaab")
Expecting:
    False
**********************************************************************
File "__main__", line 3, in NoName
Failed example:
    is_anagram("ab", "aaaab")
Expected:
    False
Got:
    True
def is_anagram(string1, string2):
    """
    This function will use the split and sort command to check to see whether one string contains all the letters of the other string.
    
    >>> is_anagram('z', 'z')
    True
    >>> is_anagram('c', 'b')
    False
    >>> is_anagram('earth','heart')
    True
    >>> is_anagram('chicken','butt')
    False
    """
    
    string1_list = list(string1)
    string1_list.sort()
    string2_list = list(string2)
    string2_list.sort()
    
    for i in range(len(string1_list)):
        if not string1_list[i] == string2_list[i]:
            return False
    return True    



doctest.run_docstring_examples(is_anagram, globals(),verbose=True)  
Finding tests in NoName
Trying:
    is_anagram('z', 'z')
Expecting:
    True
ok
Trying:
    is_anagram('c', 'b')
Expecting:
    False
ok
Trying:
    is_anagram('earth','heart')
Expecting:
    True
ok
Trying:
    is_anagram('chicken','butt')
Expecting:
    False
ok
def anagrams(first, second):
    """
    >>> anagrams("ab", "aaaab")
    False
    """

    if len(first) == len(second):
        for i in range(0,len(second)):
            if first[i] in second:
                continue
            else:
                return False
    else:
        return False 
    return True

doctest.run_docstring_examples(anagrams, globals(),verbose=True)  
#anagrams('apple', 'paple')
Finding tests in NoName
Trying:
    anagrams("ab", "aaaab")
Expecting:
    False
ok
def is_anagram(string1, string2):
    if len(string1) != len(string2):
        return False
    for letter in string1:
        if letter not in string2:
            return False
    for letter in string2:
        if letter not in string1:
            return False
    return True

print(is_anagram('dog', 'god'))
print(is_anagram('dog', 'cat'))
print(is_anagram('dogg', 'doog')) #lolnope
def is_anagram (x, y): 
    list_a = []   #Converting from a string to a list
    list_b = []   #For both x and y. 
    
    for i in x:
        list_a.append(i) #Putting the elements of the string x into list a.
    
    for i in y:
        list_b.append(i) #Putting the elements of the string y into list b.
        
    #This part was checked with a print statement.
 
    list_a.sort() #Modifies the list itself, does not return a value. 
    list_b.sort() #But for both, this function arranges the letters so that if it were an anagram they would be in the same order, which is from least to greatest in terms of their place in the alphabet.
    
    if list_a == list_b: #Rhis if statement compares the resulting lists from list sort. If they are the same, they contain the same letters, which would mean the words are anagrams. 
        ret`urn True 
    else:
        return False
    
is_anagram ('love','vole')
def is_anagram (x,y):
    x = list(x)
    y = list(y)
    x.sort()
    y.sort()
    if x == y:
        return True


is_anagram('bat', 'tab')
def anagrams(String1, String2):
    """
    Given two string inputs, checks to see if they are anagrams.
    >>>anagrams('tab', 'bat')
    True
    >>>anagrams('lad', 'bad')
    False
    """
    for letter in String1:
        if letter in String2:
            return True
        else:
            return False
        
anagrams('tab', 'bad')
import doctest
doctest.run_docstring_examples(chop, globals())
def is_anagram(s1, s2):
    s1 = s1.lower()
    s2 = s2.lower()
    s1 = s1.replace(" ", "")
    s2 = s2.replace(" ", "")

    l2 = list(s2)
    for letter in s1:
        if letter in l2:
            l2.remove(letter)
        else:
            return False
    if len(l2) == 0:
        return True
    else:
        return False

is_anagram('iceman    ', 'cinema')

Based on these answers, what is a good comprehensive list of unit tests?

Exercise 8

The (so-called) Birthday Paradox:

  1. Write a function called has_duplicates that takes a list and returns True if there is any element that appears more than once. It should not modify the original list.
  2. If there are 23 students in your class, what are the chances that two of you have the same birthday? You can estimate this probability by generating random samples of 23 birthdays and checking for matches. Hint: you can generate random birthdays with the randint function in the random module.

You can read about this problem at http://en.wikipedia.org/wiki/Birthday_paradox, and you can download Allen’s solution from http://thinkpython.com/code/birthday.py.

Most of the interesting differences here were in the has_duplicates function. Let’s start there, and then look at a few people’s complete code.

def has_duplicates(lst):
    newlist = []
    for e in lst:
        if e in newlist:
            return True
        else:
            newlist.append(e)
    return False
def has_duplicates(g):
    number = 0
    for birthday in g:   #run through every birthday
        for others in g:    #run throguh the other birthdays
            if birthday == others:   #how many bdays does it match?
                number += 1
        #print number
    if number > len(g):     #if the number is greater than 23
        return True         #then there was at least one duplicate
    else:
        return False
def has_duplicates(input_list):
    check = []
    for i in input_list:
        if i not in check:
            check.append(i)
    if len(check) < len(input_list):
        return True
    else:
        return False
def has_duplicates(list_things):
    list_things.sort()
    flag = False
    for index in range(1, len(list_things)):
        flag = flag or list_things[index] == list_things[index-1]
    return flag
            
has_duplicates([0,1,2,4, 3, 3, 3])

Careful with variable scope.

def has_duplicates(random_list):
    birthdays_sorted = sorted(birthdays)
    for i in range(1,len(random_list)):
        if random_list[i-1] == random_list[i]:
            return True
    return False
def has_duplicates(my_list):
    """
    Checks for duplicates in a list
    """
    for i in range(len(my_list)):
        if my_list[i] in my_list[i + 1:]:
            return True
    return False
def has_duplicates(birthdays):
    """
    >>> has_duplicates([1,2,3,3,4])
    True
    >>> has_duplicates(["one","two","banana"])
    False
    """
    for i in range(0,len(birthdays)):
        for j in range(0,i):
            if birthdays[i] == birthdays[j]:
                return True
    return False

What do we think about the line items = l[:]

import random

def has_duplicates(l):
    """
    Tests if a list contains duplicate values. Returns True if so.
    
    >>> has_duplicates([1,2,3,4,5])
    False
    >>> has_duplicates([1,2,3,4,2])
    True
    
    """
    items = l[:]
    items.sort()
    i = 1
    while i < len(items):
        if items[i] == items[i-1]:
            return True
        i += 1
    return False

def generate_birthdays(length):
    """
    Generates a list of random integers between 1 and 365.
    """
    result = []
    for i in range(length):
        result.append(random.randint(1, 365))
    return result

def probability_of_duplicates(n):
    """
    Empirically estimates the chance that a group of n people will
    contain at least one pair with the same birthday.
    """
    count = 0
    for i in range(1000):
        birthdays = generate_birthdays(n)
        if has_duplicates(birthdays):
            count += 1
    return count/1000.

print(probability_of_duplicates(23))

import doctest
doctest.testmod()
0.511





TestResults(failed=0, attempted=2)
import random

def has_duplicates(t):
    element_tracker = []
    for item in t:
        if item in element_tracker:
            return True
        element_tracker.append(item)
    return False

i = 0
j = 0
birthdays = []
duplicates = []

while i < 100:
    while j < 23:
        birthdays.append(random.randint(0,365)) #birthday represented as one of 365 days
        j += 1
    
    duplicates.append(has_duplicates(birthdays))
    birthdays = []
    i += 1
    j = 0

print(duplicates.count(True)) #Should be ~50