**More Complex Functions**

This discussion we're going to work on some more complicated exercises together to get more practice writing longer (or otherwise harder) functions in Python.

** Exercise 1:** Write a function

`reverse_dict`

that takes in a dictionary `d`

and reverses it. Specifically, for every `v`

in the dictionary, we want a mapping in the new dictionary that is `v: ks`

where `ks`

is a set containing all the keys that map to `v`

.(solution below)

.

.

.

.

.

.

.

In [1]:

```
def reverse_dict(d):
new_d = {}
for v in d.values():
s = {k for k, v2 in d.items() if v2 == v}
new_d[v] = s
return new_d
reverse_dict({'a': 1, 'b': 2, 'c': 2})
```

Out[1]:

In your next homework, you'll be asked to work with a dictionary by treating it as a simple directed graph. That is, you can "travel" from a key to its corresponding value, which might appear again in the dictionary as something you can travel from.

** Exercise 2:** Write a function

`edges`

that takes in a dictionary `d`

and returns a dictionary that for each element in the dictionary (key or value) associates it with the number of elements you can get either to or from it.For example, if

`graph = {'a': 'b', 'b': 'd', 'c': 'd'}`

then

`edges(graph) = {'a': 1, 'b': 2, 'c': 1, 'd': 2}`

since `a`

can reach `b`

, `b`

can be reached by `a`

and can reach `d`

, `c`

can reach `d`

, and `d`

can be reached by `b`

and `c`

.

(solution below)

.

.

.

.

.

.

.

In [2]:

```
def edges(g):
new_d = {}
for k, v in g.items():
if k in new_d.keys():
new_d[k] += 1
else:
new_d[k] = 1
if v != k: # don't count self edges twice
if v in new_d.keys():
new_d[v] += 1
else:
new_d[v] = 1
return new_d
graph = {'a': 'b', 'b': 'd', 'c': 'd'}
edges(graph)
```

Out[2]:

Higher-order functions can be pretty hard to reason about. Let's do some practice sorting lists to get a sense of how higher order functions might be used and then write one for ourselves.

Remember the Python method `list.sort()`

has an optional parameter `key`

. `key`

is a function that takes in the elements of the list and returns a number (or any other value we can compare) and sorts based on this value.

** Exercise 3:** Given a list

`l1`

of positive integers >= 10, sort them by their second digit. Then, given another list `l2`

of lowercase strings, sort them by the number of vowels ("a", "e", "i", "o", "u"). Lastly, given a third list `l3`

, use a suitable function to randomize the list.(solution below)

.

.

.

.

.

.

.

In [3]:

```
l1 = [190, 72, 81, 1000, 934]
l2 = ['pizza', 'ice cream', 'pasta']
l3 = [1, 2, 3, 4, 5]
import random
def count_vowels(x):
# can also use x.count()
total = 0
for c in x:
if c == 'a' or c == 'e' or c == 'i' or c == 'o' or c == 'u':
total += 1
return total
l1.sort(key = lambda x : int(str(x)[1]))
l2.sort(key = count_vowels)
l3.sort(key = lambda x : random.random())
print(l1)
print(l2)
print(l3)
```

** Exercise 4:** Write a function

`sort_comp`

that takes in a list `l`

and a function `comp`

. `comp`

will take in two elements from the list `a`

and `b`

and return -1, 0, or 1. If`comp(a,b) < 0`

,then assume a < b

`comp(a,b) > 0`

,then assume a > b

`comp(a,b) == 0`

,then assume a = b

(solution below)

.

.

.

.

.

.

.

In [4]:

```
def sort_comp(l, comp):
new_l = []
for elem in l:
i = 0
while i < len(new_l) and comp(elem, new_l[i]) >= 0:
i += 1
new_l.insert(i,elem)
return new_l
l = [1,4,2,3,5]
def comp_fun(a,b):
return a - b
sort_comp(l, comp_fun)
```

Out[4]: