Winter 2023-24. 90 minutes, 150 points
# CS106A Exam Reference Reminder # [square brackets] denote functions listed here that have been # mentioned but we have not used significantly. # Exam questions do not require such functions. General functions: len() int() str() float() range() list() abs() min() max() sum() sorted(lst, key=lambda, reverse=True) String functions: isalpha() isdigit() isupper() islower() find() upper() lower() split() strip() [replace()] [join()] startswith() endswith() List functions: append() index() map() [extend() pop()] Dict functions: keys() values() items() File functions with open(filename) as f: # with form for line in f: # loop over lines s = f.read() # read as one big string Lambda: lambda n: 2 * n Canvas/TK Draw # (The problem statement will mention any graphic functions # you need, so this list is just a reference.) canvas.draw_line(x1, y1, x2, y2) # x,y is upper left, optional color='red' parameter # float values are truncated to int automatically canvas.draw_rect(x, y, width, height) canvas.fill_rect(x, y, width, height) canvas.draw_oval(x, y, width, height) canvas.fill_oval(x, y, width, height) SimpleImage: # read filename image = SimpleImage(filename) # create blank image image = SimpleImage.blank(width, height) pixel = image.get_pixel(x, y) # access pixel by x,y pixel has .x .y .red .green .blue
Each of the following Python expressions evaluates to a value without error. Write the resulting Python value in each box.
>>> 75 % 10
>>> 75 // 10
>>> s = 'Python' >>> s[1:3]
>>> d = {5: 'hi', 2: 'yo', 3: 'aha', 1: 'zebra'} >>> sorted(d.keys())
One-Liners For each part, write a 1-line expression to compute the indicated value with: map() or sorted() or min() or max() or a comprehension. You do not need to call list() for these.
# a. Given a list of int values, compute a list where # each value is subtracted from 20 >>> nums = [2, -7, 20, 9] # yields: [18, 27, 0, 11]
# b. Given a list of strings, compute a list # where each new string is an uppercase version # of the original within parenthesis >>> strs = ['Is', 'Mystery', 'How'] # yields: ['(IS)', '(MYSTERY)', '(HOW)']
# c. Given a list of (x, y, z) tuples of numbers. # write a sorted/lambda expression to order the tuples # in decreasing order by z >>> tuples = [(1, 2, 3), (3, 1, 5), (2, 2, 2)] # yields: [(3, 1, 5), (1, 2, 3), (2, 2, 2)]
Given an image filename and ints a, b, and c which are 1 or more. Produce a new out image with four features: (1) a blank vertical stripe a pixels wide running down the left side of the output image. (2) at the top of the output and to the right of the a stripe, a horizontally flipped copy of the original image. (3) a second blank vertical stripe b pixels wide to the right of the flipped image. (4) a rectangle c pixels high, just below the flipped image. Add your code in the 2 indicated places to complete the function.
1. Write one line of code to create the out image.
Reminder: SimpleImage.blank(width, height)
2. Almost all of the code to write the flipped image is provided — looping over the original image, writing the colors at each "pixel" to "pixel_out". Write the one line of code which sets the "pixel_out" variable used by the later lines in this loop. The blank areas are just blank, not requiring any pixel-setting code.
def do_image(filename, a, b, c): image = SimpleImage(filename) #### 1. Create "out" image # Write flipped image to out for y in range(image.height): for x in range(image.width): pixel = image.get_pixel(x, y) #### 2. Set "pixel_out" pixel_out.red = pixel.red pixel_out.green = pixel.green pixel_out.blue = pixel.blue
a. Given a string s, return the count of the number of 'z'
chars, upper or lower case. Use any sort of loop.
'Zabzz' -> 3 'abc' -> 0
def countz(s):
b. Use s.find() to find the first '2'
in s. If the '2'
is present and the char immediately before it is a digit, return that 2-digit number, multiplied times 2. So 'ab32x'
returns 64
. In all other cases, return -1
.
'ab32x' -> 64 'xx4123xx' -> 24 'xx2' -> -1 'xxx' -> -1 '2' -> -1
def twos(s):
Given a string s, find the first double-hashtag '##'
in s. We'll call the double hashtag a. Find the first single hashtag '#'
that comes after a. We'll call this single hashtag b. If both are present, return a version of s where the text between a and b is swapped with the text after b. So for example the string 'aaa##bbbb#ccc'
returns 'aaa##ccc#bbbb'
. If a or b are not present, return the empty string.
'aaa##bbbb#ccc' -> 'aaa##ccc#bbbb' '##hello#z' -> '##z#hello' 'aaa##bbb' -> '' 'aaa#bbb' -> '' 'abc' -> ''
def swap(s):
We're working on a system that embeds "magic" tokens inside strings. Each magic token is made of any combination of digit chars and dash chars '-'
. The magic token must be 3 or more chars in length. Find the first '@@'
in s. Look for a magic token immediately before the '@@'
and return it. If there is no magic token or no '@@'
, return the empty string ''
.
'^^12-34@@77' -> '12-34' '44A-57-@@abc' -> '-57-' 'xxx123@@abc' -> '123' 'xxx12@@abc' -> '' 'xxx123@abc' -> '' 'xxx123abc' -> ''
def magic_token(s):
For this problem, you will figure out two x coordinates for a horizontal line.
The canvas contains two rectangles, the same height, next to each other The left rectangle is width a and the right rectangle is width b. We also have a number pct which is in the range 0..100 inclusive. We want to draw a horizontal line at some y value, from the rightmost column of the b rectangle towards its leftmost column, with the length of the line determined by pct. For this problem, we will only worry about the two x values required, x1 and x2.
a. What is the Python expression for the x1 value for the rightmost column of pixels of the b rectangle, in terms of a, b, and pct:
b. We want to draw a horizontal line extending towards the left side of the b rectangle, ending at x2. The length of the line is given by pct: when pct is 0, the line starts and ends in the right column. When pct is 100, the line extends towards the left, ending in the leftmost column of the b rectangle.
b. What is the the Python expression for x2, the x value where the line ends, in terms of a, b, and pct:
We have a "pairs" list of len-2 tuples. Each tuple holds a string and an int, like this:
pairs = [('donut', 127), ('tea', 62), ('nutella', 12), ('nuts', 20)]
Write code for a flatten() function that builds and returns a new list, containing all the strings and ints, keeping their original order. Omit any string less than len 4, and omit any int less than 20. So the above list produces:
['donut', 127, 62, 'nutella', 'nuts', 20]
Solve with a loop.
def flatten(pairs):
This problem is essentially the standard dict-count algorithm. Given a string s, construct and return a counts dict with a key for the lowercase form of each char in s, and its value is the count of the times that char appears in s.
'Aab7Ba' -> {'a': 3, 'b': 2, '7': 1}
def count_chars(s):
We have an "eats" dict which keeps a list of recently recommended restaurants for each key city, like this:
eats = { 'palo alto': ['teleferic', 'nolas', 'jing jing'], 'berkeley': ['top dog', 'the gilded truffle'], ... }
Write code for an add_eat() function which takes in a "line" string representing one restaurant with commas separating the parts like this: 'palo alto,3/2024,happy donuts'
. Add the city/restaurant from the string into the eats dict and return the dict. The date in the string is not used. The eats dict may be empty to start.
However, do not add the restaurant to the list if it is already in that list. Also, the restaurant list should not contain more than 10 restaurants, so do not add a restaurant that would push the length over 10.
def add_eat(eats, line):
We have a total dict, where each key is a string, and its value is a list of numbers, and a nums dict where each key is a string, and its value is a single number.
Write code for the import() function which imports the data in nums into the total dict. Consider each key/num pair in nums. We want that key and num to be in total. Add the key to total if necessary. Add the num under that key if necessary. The lists in total should include each number only once, so if a number is already present in a list, do not add a second copy of that number. You may omit the return.
total = { 'a': [1, 2] 'b': [3, 2] } nums = { 'a': 3, 'b': 2, 'c': 1 } result... total= { 'a': [1, 2, 3], 'b': [3, 2], 'c': [1] }
def import(total, nums):
You are processing data files from the BipBop video network. Each line in the file looks like, with its parts separated by commas.
kittens,sarah:34,arun:13,emily:2
The first word on the line is its tag, e.g. 'kitten'
. Following the first word will be 1 or more pairs, each representing a video clip related to that tag. Each pair, e.g. 'sarah:34'
, has a user, a colon, and the length of the video clip in seconds. The tags, user, and number strings will not contain commas or colons.
Write a function to read through all the lines, and build and return a "tags" dict with a key for each tag seen. The value for each tag should be a nested dict of all the users who appear with that tag. The value for each user in the nested dict should be the sum of the numbers seen with that tag and user. Tags and users can appear on multiple lines.
tags = { 'nutella': {'sarah': 345, 'bob': 13}, 'animals': {'emily': 231, ... }
Part-a. Write code to read in all the lines from the given filename, building and returning the "tags" dict. The basic file-reading code is provided.
def read_tags(filename): tags = {} with open(filename) as f: for line in f: line = line.strip()
# Reminder of tags structure tags = { 'nutella': {'sarah': 345, 'bob': 13}, 'animals': {'emily': 231, ...
Part-b. Write a print_tags() function that takes in the "tags" dict produced by Part-a, and prints out all the tags in sorted order, one per line. For each tag, print out all its users in sorted order, each indented by one space and followed by its sum like this:
animals arun 23 .. zelda 451 ... zebras anna 5 ...
def print_tags(tags):
Part-c. Write code for a print_user() function that takes in the tags dict and a user string, e.g. 'emily'
. Print out the tags where that user has data. Print one tag per line, in sorted order, with each tag followed by that user's sum for that tag. So for example, the user 'emily'
might produce output like:
animals 231 baseball 22 ... xylophone 6 def print_user(tags, user):
### Warmups >>> 75 % 10 5 >>> 75 // 10 7 >>> s = 'Python' >>> s[1:3] 'yt' >>> d = {5: 'hi', 2: 'yo', 3: 'aha', 1: 'zebra'} >>> sorted(d.keys()) [1, 2, 3, 5] One-liners [20 - n for n in nums] # or: map(lambda n: 20 - n, nums) ['(' + s.upper() + ')' for s in strs] sorted(tuples, key=lambda t: t[2], reverse=True) ### Image out = SimpleImage.blank(image.width + a + b, image.height + c) pixel_out = out.get_pixel(a + image.width - 1 - x, y) # or equivalently: out.width - b - 1 - x #### String-1 def countz(s): count = 0 for ch in s: # Or for/i/range if ch.lower() == 'z': count += 1 return count def twos(s): two = s.find('2') if two == -1: return -1 if two > 0 and s[two - 1].isdigit(): return int(s[two - 1:two + 1]) * 2 # A busy line return -1 #### String 2 def swap(s): a = s.find('##') b = s.find('#', a + 2) # Need the + 2 if a == -1 or b == -1: return '' return s[:a] + '##' + s[b + 1:] + '#' + s[a + 2:b] #### String 3 def magic_num(s): at = s.find('@@') if at == -1: return '' # Move start leftwards over magic chars start = at - 1 while start >= 0 and (s[start].isdigit() or s[start] == '-'): start -= 1 magic = s[start + 1:at] if len(magic) >= 3: # Equivalently: at - (start + 1) >= 3 return magic return '' ### Draw x1 = a + b - 1 x2 = a + b - 1 - (pct / 100) * (b - 1) ### List Tuple def flatten(pairs): result = [] for pair in pairs: s = pair[0] if len(s) >= 4: result.append(s) n = pair[1] if n >= 20: result.append(n) return result ### Dict-1 def count_chars(s): counts = {} for ch in s: low = ch.lower() if low not in counts: counts[low] = 0 counts[low] += 1 return counts ### Dict-2 def add_eat(eats, line): parts = line.split(',') city = parts[0] rest = parts[2] if city not in eats: eats[city] = [] inner = eats[city] # Var points to inner if rest not in inner: # Could use "and" here if len(inner) < 10: inner.append(rest) return eats ### Dict-3 def import(total, nums): for key in nums.keys(): # Make total have that key if key not in total: total[key] = [] # Think about adding n to list n = nums[key] inner = total[key] # Var points to inner if n not in inner: inner.append(n) return total ### Capstone def read_tags(filename): tags = {} with open(filename) as f: for line in f: line = line.strip() # Line like: kittens,sarah:34,arun:3 parts=line.split(',') tag = parts[0] if tag not in tags: tags[tag] = {} inner = tags[tag] # Var points to inner for s in parts[1:]: # Slice to skip 1st pair = s.split(':') user = pair[0] num = int(pair[1]) if user not in inner: inner[user] = 0 inner[user] += num return tags def print_tags(tags): for tag in sorted(tags.keys()): print(tag) inner = tags[tag] for user sorted(inner.keys()): print(' ' + user, inner[user]) def print_user(tags, user): # Look through whole dict, check for # inner with that user. for tag in sorted(tags.keys()): inner = tags[tag] if user in inner: print(tag, inner[user])