--- tags: Python --- # Python Self-Check 9 CS 1358 Introduction to Programming in Python Fall Semester 2022 Prof. Pai H. Chou Self-Check 9 Due Date: -- Answer the following questions to check your understanding of your material. Expect the same kind of questions to show up on your tests. For this course, we use vi and vim interchangeably. ## 1. Definitions and Short Answers 1. What is the equivalent lambda expression that computes the same as the following named function? ``` a. def Double(n): return n + n //lambda n:n + n b. def Bigger(a, b): return a if a > b else b //lambda a, b:a if(a > b) else b ``` 2. What lambda expression can you pass to a list's sort method's optional key plug-in function if you want to sort a list of strings by string length? For example ``` >>> L = ['an', 'apple', 'a', 'day', 'keeps', 'the', 'doctor', 'away'] >>> ***L.sort(key = lambda s:len(s))*** >>> L ['a', 'an', 'day', 'the', 'away', 'apple', 'keeps', 'doctor'] ``` which orders the strings from shortest to the longest. Fill in the yellow blank above. 3. if you want to sort a list of strings primarily by length and secondarily alphabetically (case-sensitive), what lambda would you pass to the key parameter of the list's sort method? Fill in the yellow blank below. ``` >>> L = ['a', 'glass', 'of', 'water', 'is', 'empty', 'or', 'full'] >>> ***L.sort(key = lambda s:(len(s), s))*** >>> L ['a', 'is', 'of', 'or', 'full', 'empty', 'glass', 'water'] ``` 4. Which of the following can properly sort a list of month names by month order, and why or why not? Assuming the following global symbols have been defined. ``` ML = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'] MD = {'Jan':1, 'Feb':2, 'Mar':3, 'Apr':4, 'May':5, 'Jun':6, 'Jul':7, 'Aug':8, 'Sep':9, 'Oct':10, 'Nov':11, 'Dec':12} L = [ 'Apr', 'May', 'Nov', 'Mar', 'Jan', 'Feb', 'Oct','Jun', 'Jul', 'Aug', 'Sep', 'Dec'] a. L.sort(key=ML) //Type error b. L.sort(key=MD) //Type error c. L.sort(key=lambda x: ML[x]) //Type error d. L.sort(key=lambda x: MD[x]) //L = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'] e. L.sort(key=lambda x: ML.index(x)) //L = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'] f. L.sort(key=lambda x: MD.index(x)) //AttributError dict don't have index ``` 5. If chr(97) evaluates to 'a', then what is the value of list(map(chr, [97, 98, 99, 100, 101])) ? ``` ['a', 'b', 'c', 'd', 'e'] ``` 6. What is the value of list(map(max, [1, 7, 2, 8], [5, 6, 3, 0])) ? ``` [5, 7, 3, 8] ``` 7. How do you use the built-in function zip to convert lists [1, 7, 2, 8] and [5, 6, 3, 0] into a list of tuples, as in [(1, 5), (7, 6), (2, 3), (8, 0)] ? ``` [(t) for t in zip([1, 7, 2, 8], [5, 6, 3, 0])] ``` 8. How to you write the equivalent list-comprehension version of list(map(max, [1, 7, 2, 8], [5, 6, 3, 0]))? ``` [max(*t) for t in zip([1, 7, 2, 8], [5, 6, 3, 0])] ``` 9. Suppose you want to do list(map(lambda x, y: x+y, [1, 7, 2, 8], [5, 6, 3, 0])) but replace the lambda expression (underlined above) with an existing function that does the same. What can you use instead? (Hint: import from the operator module) ``` list(map(operator.add, [1, 7, 2, 8], [5, 6, 3, 0])) ``` 10. If you want to read and print lines from a file but skip all blank lines using the following code template ``` fh = open('myfile') for line in filter(lambda ________ , fh.readlines()): print(line, end='') # no need to print extra newline fh.close() ``` What should you put as the lambda expression above? Note that a blank line consists of a single newline character. ``` filter(lambda n:n == '\n' , fh.readlines()) ``` 11. In the stack interpreter example, several versions of the interpreter are given. The if-elif version looks like this: ``` def StackInterpreter(): L = [] while True: line = input('command? ') words = line.split() if len(words) == 0: pass elif words[0] == 'show': print(L) elif words[0] == 'push': L.extend(words[1:]) elif words[0] == 'pop': print(L.pop()) elif words[0] == 'quit': break else: print('unknown command') ``` How can lines 8-24 be replaced with a check for quit followed by using the command word (i.e., words[0]) to look up and execute the corresponding action? That is, ``` if words[0] == 'quit': break D = {'show': ____, 'push': ____, 'pop': ____, } f = D.get(words[0], ______) f() ``` ``` a. on line 10' (i.e., revised line 10), what should go into the blank? Will it work if you fill in the blank on line 10' with print(L)? Why or why not? //lambda:print(L) NO b. on line 11', what should go into the blank? //lambda:L.extend(words[1:]) c. on line 12', what should go into the blank? //lambda:print(L.pop()) d. what does the D.get(key, altval) method do? How would it be rewritten without calling the .get() method? //find the value in D and return the value D[word[0]] e. what goes into the blank on line 14'? //lambda:print('unknown commend') f. can lines 10'-15' be rewritten without using temporary variables D and f? How? //D.get(words[0], lambda:print('unknown commend')) ``` 12. One alternative to lambda in the lookup table above is to use inner functions, ``` def StackInterpreter(): L = [] def show(): # inner function print(L) def push(): L.extend(words[1:]) def pop(): print(L.pop()) def unknown(): print('unknown command') D = {'show': show, 'push': push, 'pop': pop } while True: line = input('command? ') ``` ``` a. What are the inner functions in this code fragment? //show() push() pop() unknown() b. Why would it be preferable to using inner functions in this case (hint: line 11)? // 1.named, instead of exposing detail like lambda 2.has same access to parent function's local variables 3.does not pollute name space c. How would the lookup code D.get(words[0], ______) be written differently from the lambda version? Fill in the blank //D.get(words[0], lambda : unknown()) ``` 13. How can you add the documentation string (docstring) to the StackInterpreter() function above so that you can do help(StackInterpreter) in interactive mode and get the help text? ``` $ python3 -i stack.py >>> help(StackInterpreter) This is a stack interpreter. The commands are: show -- shows stack content push item1 item2 item3 -- pushes item1,... as str on stack pop -- pops and displays popped data quit -- exit interpreter END >>> ``` ``` def StackInterpreter(): """ help(StackInterpreter) This is a stack interpreter. The commands are: show -- shows stack content push item1 item2 item3 -- pushes item1,... as str on stack pop -- pops and displays popped data quit -- exit interpreter """ ``` 14. Does Python Style Guide recommend using camel case or snake case for function names? ``` snake case ``` 15. What are examples of recursive data types in Python? Are the following data types recursive? ``` One that contains substructures of the same type as itself a. int //No b. list //Yes c. tuple //Yes e. dict //Yes f. set //No g. float //No h. bool //No ``` 16. What is a recursive function? ``` a function that calls itself directly or indirectly ``` 17. What is a base case in a recursive function? Should all recursive functions have at least one base case? Why or why not? ``` return without recursive call Yes, or have infinite loop ``` 18. If you want to count the number of integers in a list that may contain either integers or list of integers and other lists (of integers and other lists…), ``` a. Can you use a loop such as follows? If not, for what cases will it fail? 1 def count_ints(L): 2 n = 0 3 for i in L: 4 if type(i) == int: 5 n += 1 6 return n //NO, [5, [6]] ``` ``` b. Can you use a loop such as follows? If not, for what cases will it fail? 1 def count_ints(L): 2 n = 0 3 for i in L: 4 if type(i) == int: 5 n += 1 6 elif type(i) == list: 7 for j in i: 8 n += 1 9 return n //NO, [21, [[5, 6], 56]] ``` ``` c. Fill in the code below for counting recursively. You may assume types of elements are either int or list. Note that this version of the code is slightly differently from the slide. 1 def count_ints(L): 2 if type(L) == int: # base case 3 return 1 4 else: 5 n = 0 6 for i in L: 7 n += count_ints(i) 8 return n ``` ``` d. Rewrite lines 5-8 above to eliminate the for loop and replace it with a combination of sum() and map(). ``` ``` def count_ints(L): if type(L) == int: # base case return 1 else: return sum(map(count, L)) ``` 19. Recursion can also replace a loop. Rewrite the count_int by converting the loop into a recursive call with its own base case (i.e., loop's terminating condition) and another recursive case for "the rest of the loop" ``` def rec_count(L): if type(L) == int: # first base case return ____ if __ : # 2nd base (L is list) so, what kind of list? return ____ return rec_count(____) + rec_count(____) # one recursive call for current element, and # 2nd recursive call for "the rest of the loop" ``` ``` def rec_count(L): if type(L) == int: # first base case return 1 if len(L) == 0 : # 2nd base (L is list) so, what kind of list? return 0 return rec_count(L[0]) + rec_count(L[1:]) # one recursive call for current element, and # 2nd recursive call for "the rest of the loop" ``` 20. Explain what the following functions do in terms of what the parameters are (if any) and what the return value is. ``` a. os.getcwd() //get the current work directory path b. os.listdir(d) //get list of names of files and directories in directory d c. os.path.isdir(d) //check if d is a directories ``` 21. To count files recursively, consider the following version of code ``` def count_files(p = '.'): import os if _____: # p is the name of a file, not a directory return 1 dir_content = ____ # get list of names (files & dir) return sum(_______) # sum recursive count of content ``` ``` a. What does '.' mean as the default value of parameter p? //current work directory path b. How do you call a function from os module to check if a path p is a file rather than a directory ("folder")? Fill in the blank on line 3. //not os.path.isdir(p) c. If p a path is a directory, how do you obtain a list of names (of files and directories) in p? Fill in the blank on line 5. //dir_content = os.listdir(p) d. How do you recursively count each path of the list so that the counts can be summed? Fill in the blank on line 6 //map(count_files, dir_content) ``` 22. In the recursive-find example, ``` a. calling M = [1, 2, [3, [4, 23]]] rec_find(M, 23) results in the tuple value (2, 1, 1). What does it mean? //23 in M[2][1][1] b. Calling rec_find(43, 43) results in True. What does it mean? //arg is not list or tuple c. What would be the result of calling rec_find([1, 2, 3], [1, 2, 3])? //True d. What would be the result of calling rec_find([[1, 2, 3]], [1, 2, 3])? //(0, ) ``` 23. The source code for the recursive-find function looks like this: ``` def rec_find(L, val): if type(L) in {list, tuple}: # look inside L for i, v in enumerate(L): p = rec_find(v, val) # recursively find item if p == True: # L[i] == val, so we return (i,) return (i,) if p != False: # L[i] recursively found val, return (i,)+p # prepend i to its path p return L == val # L not seq or for-loop didn't find ``` ``` a. What is the condition of the base case in this recursive function? //L == val b. Is line 9 executed only if type(L) is not in {list, tuple}? Or can it be executed even if type(L) is either list or tuple? If so, describe how line 9 can still be reached after executing lines 3-8? // NO YES if p is false c. Can line 9 compare only two ints, or can it be comparing two tuples or two lists? //all can d. Line 5 tests if p == True: but why can't it be replaced with if p:? //if rec_find return a tuple then if p: will be excuted e. Line 7 tests if p != False: but why isn't it redundant with line 5? Doesn't p != False imply p == True? //p can be a tuple ``` 24. In the code for indenting list items by their level of nesting, ``` def indent_list(L, level=0): if L == None: return if type(L) in {list, tuple}: for child in L: indent_list(child, level+1) else: print(f'{" "*4*level}{L}') if __name__ == '__main__': L = ['F1', ['F4', 'F5', ['F8']], 'F2', 'F3', \ 'D3', ['F6', 'F7']] indent_list(L) ``` ``` a. By the time 'F8' is printed in the test case, how many copies of indent_list calls are active? What are the values of the parameters L and level? //3 L = 'F8' level = 3 b. Is line 2 ever executed when running the test case? //No c. How many times total is indent_list(L) called in the test case above? How can you modify the code above to print your answer? //13 ``` ``` i = 0 def indent_list(L, level=0): global i i += 1 if L == None: return if type(L) in {list, tuple}: for child in L: indent_list(child, level+1) else: print(f'{" "*4*level}{L}') if __name__ == '__main__': L = ['F1', ['F4', 'F5', ['F8']], 'F2', 'F3', \ 'D3', ['F6', 'F7']] indent_list(L) print(i) ```