---
# System prepended metadata

title: Funtional Programming

---

###### tags: Python
# Funtional Programming
function / data seperation

## 4 Pillars of Functional Programming

### 1. Pure Functions
giving same input always gets the same output without having any side effect(interact with outside world)

function藉由產生新的object方式不去改變原始的data
```
def multiply_by2(li):
    new_list = []
    for item in li:
        new_list.append(item*2)
    return new_list
```

#### Example for pure function

my_list = [1,2,3]
your_list = [10,20,30]

- Map

```
map(function, data)

def multiply_by2(item):
    return item*2
    
print(list(map(multiply_by2, my_list))
```

- Filter

```
def only_odd(item):
    return item % 2 != 0
    
print(list(filter(only_odd, my_list))
```

- Zip - combine lists into tuple

`print(list(zip(my_list, your_list)))`

- Reduce

```
from functools import reduce

def accumulator(acc, item):
    return acc + item

print(reduce(accumulator, my_list, 0))
```

- Lambda - one time anonymous fuction

using the fuction only once

`lambda param: action(param)`

lambda item: item*2

### 2. Comprehension

comprehension will make codes less readable, make sure to describe it while using comprehension

- List
`my_list = [param for param in iterable]`

```
my_list = []
for char in 'hello':
    my_list.append(char)
```
    
```
my_list = [char for char in 'hello']
my_list2 = [num**2 for num in range(0,100)]
my_list3 = [num**2 for num in range(0,100) if num % 2 == 0]
```

- Set
```
my_set = {char for char in 'hello'}
my_set2 = {num**2 for num in range(0,100)}
my_set3 = {num**2 for num in range(0,100) if num % 2 == 0}
```

- Dictionary

`my_dict = {key:value** for key,value in}`

```
simple_dic = {
'a' : 1,
'b' : 2
}
```

`my_dict = {k:v**2 for k,v in simple_dict.items() if v%2==0}`

`my_dict = {num:num*2 for num in [1,2,3]}`

### 3. Decorators

Higher Order Function(HOC)
Parameters of function include function
@classmethod
@staticmethod

```
def my_decorator(func):
    def wrap_func(*args, **kwargs):
        print('*****')
        func(*args, **kwargs)
        print('*****')
    return wrap_func
    
@my_decorator
def hello(greeting, emoji=':('):
    print(greeting, emoji)
    
hello('hiiii')
```

```
def my_decorator(func):
    def wrap_func():
        print('*****')
        func()
        print('*****')
    return wrap_func
    
my_decorator(hello())
```

output:
```
*****
hello
*****
```
- Example
```
from time import time
def performance(fn):
    def wrapper(*args, **kwargs):
        t1 = time()
        result = fn(*args, **kwargs)
        t2 = time()
        print(f'took {t2-t1} s')
        return result
    return wrapper

@performance
def long_time():
    for i in range(1000000):
        i*5

long_time()
```
output:
```
took 1.9 s
```

### 4. Generator

節省記憶體空間＆更有效率
一次跑一個值出來取代原本記憶體上同位置的值

range is a generator

generator is subset of iterable

```
def generator_func(num):
    for i in range(num):
        yield i*2

g = generator_func(100)
print(g) --> output: memory position
print(next(g)) --> output: 0
```

```
def sepcial_for(iterable):
    iterator = iter(iterable)
    while True:
        try:
            print(iterator)
            next(iterator)
        except StopIteration:
            break

special_for([1,2,3])
```

```
class MyGen():
    current = 0
    def __init__(self, first, last):
        self.first = first
        self.last = last
        
    def __iter__(self):
        return self
        
    def __next__(self):
        if MyGen.current < self.last:
             num = MyGen.current
             MyGen.current +=1
             return num
        raise StopIteration
         
        
gen = MyGen(0,100)

for i in gen:
    print(i)
```

### Error Handling

```
while True:
    try:
        age = input('what is your age? ')
        10/age
        raise ValueErroe('hey cut it out')
    except ValueError:
        print('please enter a number')
        continue
    except ZeroDivisionError:
        print('please enter age higher than 0')
        break
    else:
        print('thank you!')
    finally:
        print('ok, i am finally done')
    print('can you hear me?')
```

```
def sum(num1, num2):
    try:
        return num1 + num2
    except TypeError as err:
        print(f'please enter numbers {err}')
```
        
```
def sum(num1, num2):
    try:
        return num1/num2
    except (TypeError, ZeroDivisionError) as err:
        print(err)
```

