ICS 31 -- Winter 2013 -- Quiz 6

  1. Fill in the blanks in the function definition below to make it consistent with its header and docstring. Each blank should contain just one identifier, constant, or operator. [Recall that an identifier is just a name: a variable name, a parameter name, a field/attribute name, a function name, a method name.]

    Dish = namedtuple('Dish', 'name price calories')
    Restaurant = namedtuple('Restaurant', 'name cuisine phone menu')

    def Restaurant_average_calories (R: Restaurant) -> float:
    ''' Return the average number of calories on the restaurant's menu.
    The menu is a list of Dish structures.
    '''
    return Menu_average_calories(_______________._______________)

    def Menu_average_calories(M: list) -> float:
    ''' Return the average number of calories on the menu (a list of Dishes)
    '''
    if len(M) == 0:
    return 0
    else:
    sum = _______________
    for d in _______________ :
    _______________ += _______________.calories
    return _______________ / _______________(M)
    Answer Feedback:
     from collections import namedtuple 
    # You weren't required to supply this line by the question.
    # In general on exams, you may assume that the appropriate libraries
    # have been imported. If you have a question about this on an exam,
    # you should ask.

    Dish = namedtuple('Dish', 'name price calories')
    Restaurant = namedtuple('Restaurant', 'name cuisine phone menu')

    def Restaurant_average_calories (R: Restaurant) -> float:
    ''' Return the average number of calories on the restaurant's menu.
    The menu is a list of Dish structures.
    '''
    return Menu_average_calories(R.menu)

    def Menu_average_calories(M: list) -> float:
    ''' Return the average number of calories on the menu (a list of Dishes)
    '''
    if len(M) == 0:
    return 0
    else:
    sum = 0
    for d in M:
    sum += d.calories
    return sum/len(M)

    If this was hard for you, it may be that you're not yet familiar enough with Python statements and how they're constructed.


    Take the first two blanks: We know that the Restaurant_average_calories function takes a Restaurant (which has four fields). We also know that Menu_average_calories takes a list, so that those first two blanks, the arguments in the call to Menu_average_calories, must specify a list. Where can we get a list with what we have inside Restaurant_average_calories? The only data there is R, the parameter, which is a Restaurant. The only list available is the menu field/attribute of R. So how do we get one field out of a namedtuple? With dot notation. R.menu (that's the answer) gives us the menu field of R, a list of dishes, which is the appropriate type of data to call Menu_average_calories with.


    If any terms or concepts in the preceding paragraph are unclear to you, now's the time, before the next midterm, to learn or review what they mean.


    It may also mean that you're not comfortable enough with the parts of Lab 5 that deal with dishes and collections. Processing lists, and processing lists of objects that themselves include lists, is an important aspect of the course.

  2. The Anteater Bookstore represents its inventory as a list of Book namedtuples with fields for the title, the author, the category (like Cookbook or Mystery), and the price. (The price is a float; the other fields/attributes are strings.)

    1. Write the import statement that allows the creation of namedtuples.

      Answer Feedback:
      from collections import namedtuple


      There are other variations of the import statement,
      but we've been using this one and the others require
      different ways of referring to namedtuples in the rest
      of the program.


    2. Write the namedtuple definition for a Book as described above.

      Answer Feedback:
      Book = namedtuple('Book', 'title author category price')

    3. Write an assignment statement that assigns to B a new Book namedtuple for the computer science book "Algorithms + Data Structures = Programs" by Niklaus Wirth, which sells for $23.50.

      Answer Feedback:
      B = Book('Algorithms + Data Structures = Programs', 'Niklaus Wirth',
      'computer science', 23.50)

      Note the distinction between defining the namedtuple itself as
      in part (b), which
      describes Books in general, and defining a specific instance of a Book
      as in part (c).


      Note also that the order of the fields you specified when you
      defined the namedtuple (title first, author second, and so on)
      must match the order in the Book() constructor function (as
      shown above). The problem description may not give the data
      in the same order; problem descriptions are in English, which
      allows a lot of variety in expressing things.


    4. Complete the definition of the function below, consistent with its header, docstring comment, and assertions.


      def Books_in_category(L: 'list of Book', cat: str) -> 'list of Book':
      ''' Return a list of all the books in L whose category matches cat
      '''








      assert(Books_in_category([ ], 'fiction') == [ ])

      assert(Books_in_category([Book('a', 'b', 'c', 10),
      Book('d', 'e', 'f', 20),
      Book('g', 'h', 'c', 30)], 'fiction') == [ ])

      assert(Books_in_category([Book('a', 'b', 'c', 10),
      Book('d', 'e', 'f', 20),
      Book('g', 'h', 'c', 30)], 'c')
      ==
      [Book('a', 'b', 'c', 10),
      Book('g', 'h', 'c', 30)])

      assert(Books_in_category([Book('a', 'b', 'c', 10),
      Book('d', 'e', 'f', 20),
      Book('g', 'h', 'c', 30)], 'f')
      ==
      [Book('d', 'e', 'f', 20)])
      Answer Feedback:


      def Books_in_category(L: 'list of Book', cat: str) -> 'list of Book':

      ''' Return a list of all the books in L whose category matches cat

      '''

      result = [ ]

      for b in L:

      if b.category == cat:

      result = result + [b]

      # result += [b] # alternative

      # result.append(b) # alternative

      return result

    5. Suppose we want to change the Books_in_category function above so that it returns a list of all the books in a specified category by a specified author. We'd need a third parameter to specify the author; let's call it auth. You should need to change just one line in the body of the function (i.e., not counting the function header, the docstring comment, and the assertions). Rewrite that line below.

      Answer Feedback:
      if b.category == cat and b.author == auth:
  3. Parts of this excerpt from help(str) may be useful in this problem:

    find(...)
    S.find(sub) -> int
    Return the lowest index in S where the string sub is found.
    Return -1 on failure.

    split(...)
    S.split(sep) -> list of strings
    Return a list of the words in S, using sep as the
    delimiter string.

    strip(...)
    S.strip() -> str
    Return a copy of the string S with leading and trailing
    whitespace removed.

    Complete the function definition below, consistent with its header, docstring, and assertions.

    MONTHS = ['January', 'February', 'March', 'April', 'May', 'June',
    'July', 'August', 'September', 'October', 'November', 'December']

    def mmddyy_to_MonthDayYear(mmddyy: str) -> str:
    ''' From an argument in the form '10/31/12' (month, day, year),
    return a string in the form 'October 31, 2012'. Assume all
    values are valid numbers and all years are in this century.
    '''










    assert(mmddyy_to_MonthDayYear('10/31/12') == 'October 31, 2012')
    assert(mmddyy_to_MonthDayYear('12/1/07') == 'December 1, 2007')
    assert(mmddyy_to_MonthDayYear('1/3/99') == 'January 3, 2099')
    Answer Feedback:

    def mmddyy_to_MonthDayYear(mmddyy: str) -> str:
    ''' From an argument in the form '10/31/12' (month, day, year),
    return a string in the form 'October 31, 2012'. Assume all
    values are valid numbers and all years are in this century.
    '''
    fields = mmddyy.split('/')
    month_number = int(fields[0]) - 1
    # Subtract 1 for indexing into the MONTHS list starting at 0 for January
    month_name = MONTHS[month_number]
    day = fields[1] # for clarity; could just use fields[1] in the return statement
    year = '20' + fields[2] # no need in this problem to convert to a number,
    # Also, leaving it a string helps with leading zeroes in, e.g., '12/1/07'
    return month_name + " " + day + ", " + year
  4. Suppose we have this definition:

    from collections import namedtuple
    Date = namedtuple('Date', 'year month day')

    where all three fields are numbers, so that November 12, 2012 would be represented as Date(2012, 11, 12). [You might think about how to define a function to convert a mmddyy string to a Date, or to convert a Date to a MonthDayYear string, but neither one is part of this quiz.]

    Suppose we also have this definition:

    DrivingRecord = namedtuple('DrivingRecord', 'name license age tickets')

    where name is a string representing a driver's name, license is a string representing his or her driver's licence number, age is the driver's age, and tickets is a (possibly empty) list of Date objects containing the dates on which the driver has received a traffic ticket (i.e., was cited by a police officer for violating a driving law).

    Complete the two function definitions below, consistent with their headers, docstrings, and assertions.

    def is_dangerous(d: DrivingRecord, limit: int) -> bool:
    ''' Return True if d has more tickets than the limit (and false otherwise)
    '''




    def dangerous_drivers(DRL: 'list of DrivingRecord', limit: int) -> 'list of str':
    ''' Return a list of the names of drivers in DRL who have
    more tickets than the specified limit. '''











    assert(dangerous_drivers(
    [DrivingRecord('a', '1', '16', [Date(1,2,3),Date(2,3,4),Date(3,4,5)]),
    DrivingRecord('b', '1', '66', [Date(1,2,3),Date(2,3,4)]),
    DrivingRecord('c', '1', '66', [Date(1,2,3)]),
    DrivingRecord('d', '1', '116', []),
    DrivingRecord('e', '1', '116', [Date(1,2,3),Date(2,3,4),Date(3,4,5)])], 1)
    == ['a', 'b', 'e'])
    Answer Feedback:

    def is_dangerous(d: DrivingRecord, limit: int) -> bool:
    ''' Return True if d has more tickets than the limit (and false otherwise)
    '''
    return len(d.tickets) > limit
    # Be careful to say > and not >= --- it specifies "MORE ... than the limit"

    def dangerous_drivers(DRL: 'list of DrivingRecord', limit: int) -> 'list of str':
    ''' Return a list of the names of drivers in DRL who have
    more tickets than the specified limit. '''
    result = [ ]
    for d in DRL:
    if is_dangerous(d, limit):
    result += [d.name] # or result.append(d.name)
    return result
  5. A quiz has scores in the range 0 to 10. We can represent the distribution of scores on this quiz a list of numbers, each number being the count of students who received a particular score. So in the list below, 1 person scored 0, 3 people scored 5, and 45 people scored 10:

    scores = [1, 0, 0, 2, 2, 3, 8, 22, 33, 40, 45]

    Suppose we want to print these statistics in a table in the following format:

     0.   1 ( 0.64%)
    1. 0 ( 0.00%)
    2. 0 ( 0.00%)
    3. 2 ( 1.28%)
    4. 2 ( 1.28%)
    5. 3 ( 1.92%)
    6. 8 ( 5.13%)
    7. 22 (14.10%)
    8. 33 (21.15%)
    9. 40 (25.64%)
    10. 45 (28.85%)

    In the following code, fill in each blank with one character per blank so that the output is formatted as shown above.

    TOPSCORE = 10
    for s in range(TOPSCORE + 1):
    print("{:_____d}. {:3d} ({:_____ . _____ _____}%)".format(s,
    scores[s], scores[s]/sum(scores)*100))
    Answer Feedback:
     print("{:2d}. {:3d} ({:5.2f}%)".format(s, scores[s], scores[s]/sum(scores)*100))