Practice Problems 6 Solutions

  1. Implement two different approaches to add 5 to the end of the list [1, 2, 3, 4], one that modifies the original list and the other that does not.

     # Modifies the list
     a = [1, 2, 3, 4]
     a.append(5)
        
     # Does not modify the list (because list concatenation creates a new list)
     a = [1, 2, 3, 4]
     a + [5]
    
  2. [PP Problem 8.9] Draw the memory model showing the effect of the following statements (like the figure that would be produced by ):

     values = [0, 1, 2]
     values[1] = values
    

    Check out the visualization at pythontutor.com showing the “points to” relationships. Notice the recursive structure that is created when an item in the list points to itself. This is legal in Python, but likely not a good idea.

  3. What is the value of the list a after this code executes?

     def mystery(a_list):
         a_list.sort()
         return a_list[0]
        
     a = [3, 7, 2, 9, 1]
     print("Result:", mystery(a))
    

    a will be sorted, i.e. [1, 2, 3, 7, 9] because a and a_list are aliased.

  4. What is the value of the list a after this code executes?

     def mystery(a_list):
         a_list = sorted(a_list)
         return a_list[0]
        
     a = [3, 7, 2, 9, 1]
     print("Result:", mystery(a))
    

    a will be unchanged, i.e. [3, 7, 2, 9, 1] because sorted returns a new list.

  5. What is the value of the list a after this code executes?

     def mystery(a_list):
         a_list = a_list[::]
         a_list.sort()
         a_list[1].append(2)
         return a_list[0][0]
        
     a = [[3], [7], [2], [9], [1]]
     print("Result:", mystery(a))
    

    The first line of mystery makes a “deepish” copy of the list, and sorts that copy. Thus the original list pointed to by a does not change. However, that copy is only “one level” deep. Thus modifications to the nested lists are still visible via a. The value of a is then [[3], [7], [2, 2], [9], [1]], reflecting appending the value 2 to the nested list at index 1.

  6. Draw the Python memory model after this code executed (like the figure that would be produced by ):

     a = [1, 2, [3, 4], 5]
     b = a[:]
     a[2].append(4)
    

    Check out the visualization at pythontutor.com. Notice that the slice has created a single-level copy of a, but that both a and b point to the same nested lists and so the append is visible via both a and b.

  7. What is the result of the following expressions?

    a. {2, 3, 5, 6, 7, 8}
    b. {5, 7}
    c. {2, 3}
    d. {2, 3, 6, 8}
    e. False
    f. True

  8. [PP Problem 11.1] Write a function named find_dups that takes a list of integers as its input argument and returns a set of those integers that occur two or more times in the list.

    def find_dups(int_list):
        """ 
        Return set of duplicate values
           
        Args:
            int_list: List of integers
               
        Returns:
            Set of duplicate integers
        """
        seen = set()
        repeats = set()
           
        for num in int_list:
            if num in seen:
                # We've seen num before, so it's a
                # repeat and we want to return it eventually
                repeats.add(num)
            else:
                # This is a new num so add it to the 
                # set of those we've seen
                seen.add(num)
           
        return repeats
    
  9. What is the value of the dictionary e after this code executes?

     {2: 2, 6: 4, 4: 2}
    
  10. [PP Problem 11.4] The keys in a dictionary are guaranteed to be unique, but the values are not. Write a function called count_values that takes a single dictionary as an argument and returns the number of distinct values it contains. Given the input {'red': 1, 'green': 1, 'blue': 2}, for example, it should return 2.

    def count_values(some_dict):
        """
        Counts unique values in dictionary
           
        Args:
            some_dict: dictionary
           
        Returns:
            Integer count of duplicated values
        """
           
        value_set = set()
       
        for val in some_dict.values():
            value_set.add(val)
       
        return len(value_set)
       
    # An alternate implentation that takes advantage of the set constructor
    def count_values2(some_dict):
        """
        Counts unique values in dictionary
           
        Args:
            some_dict: dictionary
           
        Returns:
            Integer count of duplicated values
        """
        return len(set(some_dict.values()))
    
  11. [PP Problem 11.5] After doing a series of experiments, you have compiled a dictionary showing the probability of detecting certain kinds of subatomic particles. The particles’ names are the dictionary’s keys, and the probabilities are the values: {'neutron': 0.55, 'proton': 0.21, 'meson': 0.03, 'muon': 0.07, 'neutrino': 0.14}. Write a function that takes a single dictionary of this kind as input and returns the particle that is least likely to be observed. Given the dictionary shown earlier, for example, the function would return ‘meson’.

    def min_key(some_dict):
        """
        Returns key with smallest value (assuming all values are probabilities)
           
        Args:
            some_dict: Dictionary with comparable values
           
        Returns:
            Key
        """
              
        # Set min_key to be a placeholder in case dictionary is empty   
        min_key = None
           
        # We don't need to initialize min_value before loop since 
        # we are guaranteed to initialize it before use
           
        for key in some_dict:
            if min_key == None or some_dict[key] < min_value:
                min_key = key
                min_value = some_dict[key]
           
        return min_key
    
  12. You want to determine if all of the keys of a dictionary are integers. Write a function named all_ints that takes a dictionary as a parameter and returns True if all the keys are integers and False otherwise. For example:

     >>> all_ints({ 1: 2, 2: "a" })
     True
     >>> all_ints({ 1: 2, 2: "a", "c": 3 })
     False
    

    As a hint, recall that you can use the type function to determine the type of a value, and that you can perform equality comparisons on those types, e.g.

     >>> type(1) == int
     True
     >>> type(1) == float
     False
     >>> type("a") == int
     False
    
     def all_ints(dictionary):
         # Iterate through all keys in the dictionary
         for key in dictionary.keys():
             if type(key) != int:
                 # As soon we we find one non-int, then we know the
                 # function should return False (and we can stop)
                 return False
                
         # At this point we have iterated through all the keys and all must
         # have been integers (or we would have returned False), and thus we
         # know we can return True
         return True