Differences
This shows you the differences between two versions of the page.
| Both sides previous revision Previous revision Next revision | Previous revision | ||
|
lfa:lab01-python-intro [2021/09/08 09:42] pdmatei |
lfa:lab01-python-intro [2021/10/07 11:32] (current) ioana.georgescu [List comprehensions] |
||
|---|---|---|---|
| Line 12: | Line 12: | ||
| ==== List traversal ==== | ==== List traversal ==== | ||
| - | 1.1. Write a function ''f(l)'' which determines the maximum of a list ''l'' of integers. | + | **Exercise 1.1.** Write a function ''f(l)'' which determines the maximum of a list ''l'' of integers. |
| <hidden The pythonic way> | <hidden The pythonic way> | ||
| Line 23: | Line 23: | ||
| </hidden> | </hidden> | ||
| - | 1.2. Modify the previous function to ''f(l,start,stop)'' and determine the maximum between positions ''start'' and ''stop''. | + | **Exercise 1.2.** Modify the previous function to ''f(l,start,stop)'' and determine the maximum between positions ''start'' and ''stop''. |
| <hidden The pythonic way> | <hidden The pythonic way> | ||
| When we care about the indices of a list, we can use the function ''range(x,y)'' which returns **a list of integers** starting from ''x'' until ''y-1''. | When we care about the indices of a list, we can use the function ''range(x,y)'' which returns **a list of integers** starting from ''x'' until ''y-1''. | ||
| <code python> | <code python> | ||
| - | for elem in collection: | + | for i in range(0,len(collection)): |
| - | # do something | + | # do something with collection[i] |
| </code> | </code> | ||
| - | This idiom can be used for any collection not just lists, as you will discover during the lab. | ||
| </hidden> | </hidden> | ||
| - | Loops (for … in …) | + | ==== Slicing lists ==== |
| - | Accessing (elements of an array, as well as slicing) | + | |
| - | Inner functions | + | |
| - | List comprehensions, filters | + | |
| - | Stacks | + | |
| - | Dictionaries | + | |
| - | Pairs | + | |
| - | Classes and inheritance, instance-of | + | |
| - | toString | + | |
| - | Higher-order functions and lambdas | + | |
| - | Unpacking (for tuples, lists) | + | |
| + | **Exercise 1.3.** Find the **longest** sequence of positive integers from a list. E.g. for the list ''[1,3,-1,2,0,1,5,4,-2,4,5,-3,0,1,2]'' the answer is ''[2,0,1,5,4]''. | ||
| - | <hidden The Pythonic way> This text will be hidden <code python> solution </code> </hidden> | + | <hidden The pythonic way> |
| + | Python supports various list slicing operations, as well as "overloaded" indexing: | ||
| + | <code python> | ||
| + | # the sublist from positions 0 to x of l: | ||
| + | l[0:x] | ||
| + | # the very same list: | ||
| + | l[:x] | ||
| + | # the last element of a list: | ||
| + | l[-1] | ||
| + | # the last three elements of a list: | ||
| + | l[-3:] | ||
| + | # the slice from n-3 to n-1, where n is the number of elements from the list | ||
| + | l[-3:-1] | ||
| + | </code> | ||
| + | </hidden> | ||
| + | **Exercise 1.4.** Write a pythonic function to check if a list is palindrome. | ||
| - | Python is an interpreted, dynamically typed language which is easy to use for scripting and prototyping applications. | + | ===== Other datatypes ===== |
| - | C and Java programmers quickly adjust to the Python syntax. Unlike C, in Python, lists are predefined and usually more used than arrays: | + | The most widely used datatypes in Python are lists and dictionaries. Additionally, at LFA, we will also use: **sets**, **stacks**. |
| + | ==== Stacks ==== | ||
| + | |||
| + | In Python, **lists** are also used as stacks: | ||
| <code python> | <code python> | ||
| - | # Comments begin with a '#' and end at the end of the line | + | l = [] |
| - | l = [] # comment | + | l.append(x) # add x at the TOP of the stack (this will be the last element of the list) |
| - | l.append("1") | + | l[-1] # this is the top of the stack |
| - | l.append(0) | + | x = l.pop() # removes and returns an element from the top of the stack. |
| - | l.append([]) | + | </code> |
| - | print(l) | + | **Exercise 1.5.** Write a function which determines (and returns) the **longest** sequence of consecutive integers from a list. |
| - | </code> | + | |
| - | **Remarks** | + | ==== Dictionaries ==== |
| - | * Although Python is strongly-typed (the interpreter keeps track and verifies the type of objects), lists may have different 'types' of elements. | + | |
| - | * There exist two different major versions of Python: Python2 and Python3. Although similar, there are some incompatible differences between them. We will use Python3, so take care when researching documentation. | + | Python dictionaries are actually **hash maps** (or **hash tables**). The keys ''k'' are **dispersed** using a hash-function into buckets. Each bucket will hold its respective values. Some examples of dictionary usage: |
| <code python> | <code python> | ||
| - | for i in range(0,len(l)): | + | #create an empty dictionary: |
| - | print(l[i]) | + | d = {} |
| - | print(range(0,len(l))) | + | #add or modify a key-value: |
| + | d[k]=v | ||
| - | for elem in l: | + | #search if a key k is defined in a dictionary d: |
| - | print(elem) | + | if k in d: |
| + | # do something | ||
| </code> | </code> | ||
| - | **Remarks** | ||
| - | * List traversal may be achieved using indexing. Indexes are integers taken from a range. ''range(0,len(l))'' is a list in itself. | ||
| - | * Lists (and other data structures) can be iterated using ''in'' | ||
| - | |||
| - | **Syntax** | ||
| - | * unlike C or Java where we often use ''{...}'' for scoping, in Python we use indentation levels (either a tab or 4 spaces) | ||
| - | * control instructions such as ''for'', ''while'', ''if'' do not require ''(...)'' for conditions but they must end with '':'' | ||
| + | **Exercise 1.6.** Write a function which determines the number of occurrences of each character in a string. | ||
| + | <hidden The pythonic way> | ||
| <code python> | <code python> | ||
| - | def func(l1, l2): | + | def count(l): |
| - | l1.append(3) | + | d = {} |
| - | return l1 + l2 | + | for x in l: |
| - | + | if x in d: | |
| - | x = [1] | + | d[x] += 1 |
| - | y = [2] | + | else: |
| - | print(func(x,y)) | + | d[x] = 1 |
| - | print(x) | + | return d |
| </code> | </code> | ||
| + | </hidden> | ||
| - | **Remarks** | + | ==== Sets ==== |
| - | * although it is possible to add type annotations, in Python a function's signature only consists of the number of parameters and their names | + | |
| - | * objects are generally passed as reference (hence, when printing ''x'', we see the list ''[1,3]'') (for details see: [[https://docs.python.org/3/reference/datamodel.html | Python data model]]) | + | |
| - | * ''+'' denotes list concatenation | + | |
| - | **Exercise 1** | + | Sets are collections where each element is unique. Sets can be traversed and indexed exactly as lists. They are initialised using the constructor ''set'': |
| - | Write a function which prints EACH repeating character from a string. (Hint: strings are lists of characters). | + | <code python> |
| + | # the empty set: | ||
| + | s = set() | ||
| - | ==== Useful data structures: dictionaries and sets ==== | + | # set constructed from a list: |
| + | s = set([1,2,3]) | ||
| - | Dictionaries are another useful data structure. A dictionary is a ''<key> : <value>'' mapping. Unlike lists, keys may be of any type (integers, strings, or any other datatype). | + | # adding a new element to the set: |
| + | s.add(4) | ||
| + | </code> | ||
| + | **Exercise 1.7.** Determine the list of characters which occur in a string. E.g. for "limbajeformale" we have: "limbajefor". (Hint 1: in Python there is no difference between a character and a singleton string and strings are just lists; Hint 2: to create a list from another object, use ''list()''). | ||
| + | |||
| + | ==== Pairs ==== | ||
| + | |||
| + | Pairs are very similar to lists and can be substituted by the latter in many cases: | ||
| <code python> | <code python> | ||
| - | d = {} | + | p = (x, y) # a pair where the first element is x and the second is y |
| - | d["X"] = ["X"] | + | t = (x, y, z) # a tuple |
| - | if "X" in d: | + | fst = p[0] |
| - | print("d[X] is defined in the dictionary") | + | snd = p[1] |
| - | + | ||
| - | if not "Y" in d: | + | for k in t: |
| - | print("d[Y] is not defined in the dictionary") | + | # do something with element k of the tuple |
| + | | ||
| </code> | </code> | ||
| - | **Exercise 2** Write a function returns the number of repetitions of each character from a text. (Hint: use dictionaries to store the number of repetitions.) | + | ===== The functional style ===== |
| - | **Exercise 3** Write a function returns the number of unique characters from a list. | + | ==== Higher-order functions and lambdas ==== |
| - | Remark: | + | The most used higher-order functions in Python are **map** and **reduce**: |
| - | * a simpler way to ensure uniqueness is to use sets. | + | <code python> |
| - | * a set may be created from a list: ''s = set([1,2,3,1])'' | + | from functools import reduce |
| - | * and in turn, a list may be created from a set: ''l = list(s)'' | + | |
| - | **Exercise 4** Write a function which takes a pattern and a text and prints all indexes where an occurrence of pattern in text are found. | + | # adds 1 to each element of a list |
| + | def allplus1(l): | ||
| + | return map(lambda x:x+1,l) | ||
| - | Remark: | + | # computes the sum of elements of a list |
| - | * lists can be sliced in Python using the following syntax: ''l[start_index:end_index]'' Test it to see how slicing is performed. | + | def sum_l (l): |
| + | # inner functions are very useful for defining local, reusable functionality | ||
| + | def plus(x,y): | ||
| + | return x + y | ||
| + | return reduce(plus,l) # reduce is a little different from Haskell folds: it does not use an initial value | ||
| + | |||
| + | def short_sum(l): | ||
| + | return reduce(lambda x,y:x+y,l) # observe the syntax for lambdas | ||
| + | </code> | ||
| - | **Exercise 5** Modify the previous implementation to use slicing. | + | ==== List comprehensions ==== |
| - | Remark: | + | List comprehensions are widely used programming tools in Python. Usage examples: |
| - | * in Python we can use arbitrarily nested functions | + | |
| - | **Exercise 6** Write a function which searches for a list of patterns in a text. | ||
| <code python> | <code python> | ||
| - | def find_patterns (pattern_list, text): | + | # adding 1 to each element of a list |
| - | # checks if pattern is found at position index in text | + | l1 = [x+1 for x in [1,2,3]] |
| - | def inner_search (pattern,index): | + | # [2,3,4] |
| + | |||
| + | # packing elements into pairs | ||
| + | l2 = [(x,x+1) for x in [1,2,3]] | ||
| + | # [(1,2), (2,3), (3,4)] | ||
| + | |||
| + | # unpacking pairs in the for notation | ||
| + | l3 = [x+y for (x,y) in [(1,2), (2,3), (3,4)]] | ||
| + | # [3,5,7] | ||
| + | |||
| + | # combined list comprehensions | ||
| + | l4 = [(x,y) for x in [1,2,3] for y in [4,5,6]] | ||
| + | # [(1, 4), (1, 5), (1, 6), (2, 4), (2, 5), (2, 6), (3, 4), (3, 5), (3, 6)] | ||
| + | |||
| + | # filters | ||
| + | l5 = [x for x in [1,2,3,4] if x>2] | ||
| + | # [3,4] | ||
| </code> | </code> | ||
| - | Remark: | + | **Exercise 1.8.** Write a function which takes a list of records //first name, last name, CNP// (encoded as tuples), and returns a list of **the last names** and **ages** of all females which are younger than the average of the entire list. E.g. ''[ ("Mary", "Smith", "2030602123456"), ("Anne", "Doe", "2121092123456"), ("Matei", "Dan", "1121202123456"), ("Maggie", "Byrne", "2121078123456")]'' yields ''[("Smith",19), ("Doe",29)]''. Maggie was born in '78, whereas Mary, Anne and Matei were born in '94, '92 and 2002, respectively. |
| - | * Python supports functional-style programming to some extent. | + | |
| + | <hidden The pythonic way> | ||
| <code python> | <code python> | ||
| - | def plus1(x): | + | from functools import reduce |
| - | return x + 1 | + | |
| + | def getYouth(l): | ||
| + | # this function computes the age of a given CNP | ||
| + | def age(cnp): | ||
| + | # conversion to integer of the two-character year code | ||
| + | if int(cnp[5:8]) <= 21: | ||
| + | return 2021 - int("20"+cnp[5:7]) | ||
| + | else: | ||
| + | return 2021 - int("19"+cnp[5:7]) | ||
| | | ||
| - | print(map(plus1,[1,2,3])) | + | # computing the average ages (a map could have also been used) |
| - | print(map(lambda x:x+1, [1,2,3])) | + | avg = reduce(lambda a,b:a+b, [age(x[-1]) for x in l]) / len(l) |
| + | |||
| + | # we return the last name and the age of the filtered list l | ||
| + | return [(ln,age(cnp)) for (fn,ln,cnp) in l if cnp[0]=='2' and age(cnp) <= avg] | ||
| </code> | </code> | ||
| + | </hidden> | ||
| - | **Exercise 8** Modify the previous implementation and instead of ''for'', use ''map'' (cast the return of ''map'' to ''list'': ''list(map(...))'') | ||
| - | However, it is more common in Python to employ //list comprehensions// instead of ''map'': | ||
| - | <code python> | ||
| - | def plus1(x): | ||
| - | return x + 1 | ||
| - | | ||
| - | print([plus1(x) for x in [1,2,3]]) | ||
| - | print([(x + 1) for x in [1,2,3]])) | ||
| - | </code> | ||
| - | List comprehensions also support the functionality of ''filter'': | ||
| - | <code python> | ||
| - | print([(x+1) for x in [1,2,3,4,5,6] if (x % 2 == 0)]) | ||
| - | </code> | ||
| - | **Exercise 9** Modify the previous implementation and instead of ''for'', use list comprehensions. | + | |
| + | |||
| + | |||
| + | /* | ||
| ==== Classes and inheritance ==== | ==== Classes and inheritance ==== | ||
| Line 239: | Line 279: | ||
| * Extend the class example shown previously to include class ''Node'' which models non-empty trees. Implement methods ''size'' and ''contains''. | * Extend the class example shown previously to include class ''Node'' which models non-empty trees. Implement methods ''size'' and ''contains''. | ||
| + | |||
| + | */ | ||
| + | |||
| + | ===== Practice ===== | ||
| + | |||
| + | A labelled graph is encoded as a file where: | ||
| + | * **the first line** consists of the number of nodes | ||
| + | * **each subsequent line** is an edge ''<from> <label> <to>'' | ||
| + | Example: | ||
| + | <code> | ||
| + | 5 | ||
| + | 0 X 1 | ||
| + | 1 O 2 | ||
| + | 1 X 3 | ||
| + | 1 O 4 | ||
| + | 4 X 1 | ||
| + | 3 O 2 | ||
| + | </code> | ||
| + | |||
| + | * Suppose we encode streets as labelled graphs, where each label 'X' or 'O' denotes if a street is closed or open. | ||
| + | * Compute the set of accessible nodes from a given **source**, via open streets. | ||
| + | * (Hint1: google //Python read lines// to see how to read from a file; also, google ''split'' in Python) | ||
| + | * (Hint2: you will need a dictionary to store, for each node and label l, the list of its l-successors) | ||
| + | |||
| + | |||
| + | ===== Haskell practice ===== | ||
| + | |||
| + | Solve the same exercise, only build your own input as a string, instead of a file. | ||