CS106AP Midterm from Winter 2018-19. The median was 83/100 and we curved the grades.

CS106AP Midterm - Solution

Instructions

# CS106AP Exam Reference Reminder
# [square brackets] denote functions listed here that we have not used yet
# Exam questions will not depend on functions we have note used yet
General functions:
  len() int() str() range() list() sorted()
String functions:
  isalpha() isdigit() isspace() isupper() islower()
  find() upper() lower() strip()
List functions:
  append() extend() [pop() insert()]
Dict functions:
  keys() [values() items()]

SimpleImage:
  # read filenae
  image = SimpleImage(filename)
  # create blank image
  image = SimpleImage.blank(width, height)

  # foreach loop
  for pixel in image:

  # range/y/x loop
  for y in range(image.height):
      for x in range(image.width):
          pixel = image.get_pixel(x, y)

Read lines out of a text file:
  with open(filename, 'r') as f:
      for line in f:

2. Graphics1 (15 points)

The provided code loads 2 images into variables "image" and "back". These images are the same size. Write code to change "image" as follows: Detect pixels in image where the red value is greater than 100. Replace these red pixels with grayscale versions of the corresponding pixels from the back image.

def image2(file1, file2):
    image = SimpleImage(file1)
    back = SimpleImage(file2)
    for pixel in image:



3. Graphics2 (15 points)

Given an image and a positive int n, create a new "out" image made of n upside down copies of the original in a horizontal row.

def n_upside_down(filename, n):
    image = SimpleImage(filename)



4. Loops (15 points)

Given a non-negative int n, return a list of n len-5 lists, like these examples:

n=1 -> [[1, 0, -1, -2, -3]]
n=2 -> [[1, 0, -1, -2, -3], [2, 1, 0, -1, -2]]
n=3 -> [[1, 0, -1, -2, -3], [2, 1, 0, -1, -2], [3, 2, 1, 0, -1]]
def fives(n):



5. Parse1 (20 points)

Given a string s. For each '@' in s, parse out the substring of zero or more alphabetic chars which immediately follow the '@', which we will call a "word". Return a list of all the word substrings in the order they appear in s, like these examples:

'x @abc @xyz x' -> ['abc', 'xyz']
'xx@xx$$' -> ['xx']
'@a @ @c' -> ['a', '', 'c']
def parse1(s):



6. Parse2 (20 points)

For this problem, we'll say that the text of an int "number" can optionally contain commas, like '1,200' or '2,,,3,4' or '42'. The rightmost char of the number will always be a digit, but everywhere else there can be commas mixed in.

Given a string s, find the last number-with-commas in s, and return it as a string. If there is no such number in s, return the empty string.

So for example ...

'xx 11 22 1,200 xx' -> '1,200'
'xx 11 22 yyy' -> '22'
'xx 1,2 x ,99,,2,3 z' -> ',99,,2,3'
'abcxyz' -> ''
def parse2(s):



7. Dict (20 points)

We'll say that the "prefix" of a word is its first 2 chars, in lowercase form. Given a list of words, we want to count how many times each prefix appears. So for example if the words are

['baby', 'Aardvark', 'basketball']

We'd count that the prefix 'ba' appears 2 times, and 'aa' 1 time.

count_prefix(words): given a "words" list of word strings. Build a dict to count how many times each prefix appears across all the words. Ignore words with length less than 2. Once the dict is complete, print out, in alphabetical order, each prefix followed by its count, 1 per line, like this:

aa 6
ab 17
ax 1
ba 12
...
def count_prefix(words):




Solutions

1. Image2
# interesting: this is quite straightforward, if logic, compute avg of correct thing
def image2(file1, file2):
    image = SimpleImage(file1)
    back = SimpleImage(file2)
    for pixel in image:
        if pixel.red > 100:
            other = back.get_pixel(pixel.x, pixel.y)
            avg = (other.red + other.green + other.blue) // 3
            pixel.red = avg
            pixel.green = avg
            pixel.blue = avg


2. N Upside Down
# interesting: correct output size, 3 loops, forming pixel_out x,y is HUGE
def n_copies(filename, n):
    image = SimpleImage(filename)
    out = SimpleImage.blank(image.width * n, image.height)
    
    for i in range(n):  # copy #i of original (either loop order works)
        for y in range(image.height):  # loop over orig pixels
            for x in range(image.width):
                pixel = image.get_pixel(x, y)
            
                left_x = i * image.width  # left edge of copy i
                # most of the points are on this line:
                pixel_out = out.get_pixel(left_x + x, image.height - y - 1)
                pixel_out.red = pixel.red
                pixel_out.green = pixel.green
                pixel_out.blue = pixel.blue


3. Fives
"""
# interesting, lots of OBO bounds to get here
def fives(n):
    """Given a non-negative int n, return a list of n len-5 lists, like this:
    [[1, 0, -1, -2, -3], [2, 1, 0, -1, -2], [3, 2, 1, 0, -1], ...] Use nested range loops.
    >>> fives(1)
    [[1, 0, -1, -2, -3]]
    >>> fives(2)
    [[1, 0, -1, -2, -3], [2, 1, 0, -1, -2]]
    >>> fives(3)
    [[1, 0, -1, -2, -3], [2, 1, 0, -1, -2], [3, 2, 1, 0, -1]]
    """
    outer = []
    for start in range(1, n + 1):
        inner = []
        for i in range(start, start - 5, -1):
            inner.append(i)
        outer.append(inner)
    return outer

4. Parse1
# basically off of hw/lecture
def parse1(s):
    """
    >>> parse2('xx@abc @xyz')
    ['abc', 'xyz']
    """
    search = 0
    words = []
    while True:
        at = s.find('@', search)
        if at == -1:
            break

        # pass over alpha chars to find end
        end = at + 1
        while end < len(s) and s[end].isalpha():
            end += 1

        word = s[at + 1:end]
        words.append(word)

        # Set up next iteration
        search = end
    return words

5. Parse2
# interesting: loop to find right (not), loop to find left (or), slice OBO
def foo(s):
    """
    right-left digit/or
    could do that emails one!
    >>> foo('xx1,2,3xx')
    '1,2,3'

    """
    right = s.len() - 1
    while right >=0 and not s[right].isdigit():
        right -= 1

    if right == -1:
        return ''

    left = right - 1
    while left >= 0 and (s[left].isdigit() or s[left]==','):
        left -= 1

    return s[left+1:right+1]


6. Count Prefix
# 6. can they do straighforward just like hw, if/empty/count algo, slice/lower, output
def count_prefix(words):
    """
    >>> count_prefix(['aaa', 'BBB', 'a', 'aaa'])
    aa 2
    bb 1
    """
    counts = {}
    for word in words:
        if len(word) >= 2:
            pre = word[:2].lower()
            if pre not in counts:
                counts[pre] = 0
            counts[pre] += 1
    for pre in sorted(counts.keys()):
        print(pre, counts[pre])