1. Functional Programming

1.1. Rekurencja

  • warunek zakończenia

  • maksymalna ilość zagłębień

def factorial(n: int) -> int:
    if n == 0:
        return 1
    else:
        return n * factorial(n-1)

1.2. Lambda - funkcje anonimowe

lista = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]


def parzystosc(x):
    if x % 2 == 0:
        return True
    else:
        return False


parzyste1 = filter(lambda x: x % 2 == 0, lista)
parzyste2 = filter(parzystosc, lista)

print(list(parzyste1))
print(list(parzyste2))
class Osoba:
    pass

o = Osoba()
o.say_hello = lambda: print('hello')

o.say_hello()
DATA = [
    {'user': 'jkowalski', 'uid': 1000},
    {'user': 'root', 'uid': 0},
]


system_users = filter(lambda x: x['uid'] < 1000, DATA)
out = list(system_users)
print(out)





def is_system_user(data):
    if data['uid'] < 1000:
        return True
    else:
        return False

system_users = []
for user in DATA:
    if is_system_user(user):
        system_users.append(user)

print(system_users)

1.3. Closure

def f(x):
    def g(y):
        return x + y
    return g

1.4. Monady

  • Monady pozwalają programiście sprzęgać ze sobą kolejno wykonywane działania i budować potoki danych, w których każda akcja jest materializacją wzorca dekoratora z dodatkowymi regułami przetwarzającymi.

1.5. złożenia funkcji

1.5.1. map()

lista = [1, 2, 3]

def inkrementuj(y):
    return 1 + y

map(inkrementuj, lista)
map(lambda y: 1 + y, l)
def kwadrat(x):
    return pow(x, 2)

potegi1 = map(kwadrat, dane)
potegi2 = map(lambda x: pow(x, 2), dane)

print(list(potegi1))
import datetime

def opoznienie(przesuniecie):
    delay = pow(przesuniecie, 2)
    return datetime.datetime.now() + datetime.timedelta(seconds=delay)

czasy = map(opoznienie, dane)

print(list(czasy))

1.5.2. zip()

x = [1, 2, 3]
y = [4, 5, 6]

zipped = zip(x, y)
list(zipped)
# [(1, 4), (2, 5), (3, 6)]
# unzip
x2, y2 = zip(*zip(x, y))

x == list(x2) and y == list(y2)  # True

1.5.3. filter()

OSOBY = [
    {'imie': 'José', 'wiek': 10},
    {'imie': 'Max', 'wiek': 18},
    {'imie': 'Иван', 'wiek': 21},
]

def osoba_pelnoletnia(osoba):
    if osoba['wiek'] >= 18:
        return True
    else:
        return False


dorosli = filter(osoba_pelnoletnia, OSOBY)
print(list(dorosli))
def parzysta(liczba):
    if liczba % 2 == 0:
        return True
    else:
        return False


dane = range(0, 30)

parzyste1 = filter(parzysta, dane)
parzyste2 = filter(lambda x: x % 2 == 0, dane)
parzyste3 = filter(lambda x: not x % 2, dane)

print(list(parzyste3))

1.5.4. all(iterable)

Return True if all elements of the iterable are true (or if the iterable is empty). Equivalent to:

def all(iterable):
    for element in iterable:
        if not element:
            return False
    return True

1.5.5. any(iterable)

Return True if any element of the iterable is true. If the iterable is empty, return False. Equivalent to:

def any(iterable):
    for element in iterable:
        if element:
            return True
    return False

1.5.6. enumerate(iterable, start=0)

Return an enumerate object. iterable must be a sequence, an iterator, or some other object which supports iteration. The __next__() method of the iterator returned by enumerate() returns a tuple containing a count (from start which defaults to 0) and the values obtained from iterating over iterable.

seasons = ['Spring', 'Summer', 'Fall', 'Winter']

list(enumerate(seasons))
# [(0, 'Spring'), (1, 'Summer'), (2, 'Fall'), (3, 'Winter')]

list(enumerate(seasons, start=1))
# [(1, 'Spring'), (2, 'Summer'), (3, 'Fall'), (4, 'Winter')]

Equivalent to:

def enumerate(sequence, start=0):
    n = start
    for elem in sequence:
        yield n, elem
        n += 1

1.6. functools

import functools

my_list = [1, 2, 3, 4, 5]

def add_it(x, y):
    return (x + y)

sum = functools.reduce(add_it, my_list)
print(sum)
square = lambda x: x**2
double = lambda x: x + x

print(list(map(square, my_list)))
print(list(map(double, my_list)))
import functools

my_list = [1, 2, 3, 4, 5]
sum = functools.reduce(lambda x, y: x + y, my_list)
print(sum)

1.6.1. memoize

import functools

@functools.lru_cache(maxsize=None)
def fib(num):
    if num < 2:
        return num
    else:
        return fib(num-1) + fib(num-2)
def factorial(n):
    if not hasattr(factorial, 'mem'):
        factorial.mem = {1: 1}
    if not n in factorial.mem:
        factorial.mem[n] = n * factorial(n - 1)
    return factorial.mem[n]
def memoize(function):
    from functools import wraps

    memo = {}

    @wraps(function)
    def wrapper(*args):
        if args in memo:
            return memo[args]
        else:
            rv = function(*args)
            memo[args] = rv
            return rv
    return wrapper


@memoize
def fibonacci(n):
    if n < 2: return n
    return fibonacci(n - 1) + fibonacci(n - 2)

fibonacci(25)

1.7. Callback

def http(obj):
    response = requests.request(
        method=obj.method,
        data=obj.data,
        path=obj.path)

    if response == 200:
        return obj.on_success(response)
    else:
        return obj.on_error(response)


class Request:
    method = 'GET'
    path = '/index'
    data = None

    def on_success(self, response):
        print('Success!')

    def on_error(self, response):
        print('Error')

http(
    Request()
)

1.8. Assignments

1.8.1. map(), filter() i lambda

  • Filename: funcprog_map_filter_lambda.py

  • Lines of code to write: 10 lines

  • Estimated time of completion: 15 min

  1. Używając generatora zbuduj listę zawierającą wszystkie liczby podzielne przez 3 z zakresu od 1 do 33:

  2. Używając funkcji filter() usuń z niej wszystkie liczby parzyste

  3. Używając wyrażenia lambda i funkcji map() podnieś wszystkie elementy tak otrzymanej listy do sześcianu

  4. Odpowiednio używając funkcji sum() i len() oblicz średnią arytmetyczną z elementów tak otrzymanej listy.

1.8.2. Zbalansowanie nawiasów

  • Filename: funcprog_brackets.py

  • Lines of code to write: 10 lines

  • Estimated time of completion: 15 min

  1. Napisz kod, który za pomocą rekurencji sprawdzi zbalansowanie nawiasów, tzn. czy ilość otwieranych nawiasów jest równa ilości nawiasów zamykanych.

  2. Zwórć uwagę, że mogą być cztery typy nawiasów:

    1. okrągłe: ( i )

    2. kwadratowe: [ i ]

    3. klamrowe { i }

    4. trójkątne < i >

def zbalansowanie_nawiasow(ciag_znakow: str) -> bool:
    """
    >>> zbalansowanie_nawiasow('{}')
    True
    >>> zbalansowanie_nawiasow('()')
    True
    >>> zbalansowanie_nawiasow('[]')
    True
    >>> zbalansowanie_nawiasow('<>')
    True
    >>> zbalansowanie_nawiasow('')
    True
    >>> zbalansowanie_nawiasow('(')
    False
    >>> zbalansowanie_nawiasow('}')
    False
    >>> zbalansowanie_nawiasow('(]')
    False
    >>> zbalansowanie_nawiasow('([)')
    False
    >>> zbalansowanie_nawiasow('[()')
    False
    >>> zbalansowanie_nawiasow('{()[]}')
    True
    >>> zbalansowanie_nawiasow('() [] () ([]()[])')
    True
    >>> zbalansowanie_nawiasow("( (] ([)]")
    False
    """
    pass