<jupyter_start><jupyter_text>Functional programming style  Applying function to iterables Consider the following lists as running examples.<jupyter_code>import random
l1 = list(range(10))
l2 = random.choices(range(3), k=7)
print(l1, l2)<jupyter_output>[0, 1, 2, 3, 4, 5, 6, 7, 8, 9] [0, 0, 1, 0, 1, 0, 0]<jupyter_text>The `map` function can be used to apply a function to any element of an iterable, e.g.,<jupyter_code>list(map(str, map(lambda x: x % 2 == 0, l1)))<jupyter_output><empty_output><jupyter_text>Note that the result of `map` is not a list, but rather an iterator, even when it is appllied to a list. The `filter` function applies a Boolean predicate to each element of an iterable, and retains only those elements for which the predicate evaluates to `True`.  In the example below, only the even elements will be returned.<jupyter_code>list(filter(lambda x: x % 2 == 0, l1))<jupyter_output><empty_output><jupyter_text>Python has quite number of reduction functions such as `sum`, `max`, and `min`, however, using the `reduce` function in `functools` we can easily write our own.<jupyter_code>from collections import Counter
from functools import reduce
def incr(c, x):
    c[x] += 1
    return c
reduce(incr, l2, Counter())<jupyter_output><empty_output><jupyter_text>However, note that there is no performance advantage in using `reduce` for this particular case:<jupyter_code>l_long = random.choices(range(5), k=1_000_000)
%timeit reduce(incr, l_long, Counter())
%%timeit
counter = Counter()
for x in l_long:
    counter[x] += 1<jupyter_output>1.04 s ± 87.3 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)<jupyter_text>Higher order functions A new function can be created out of an existing function using `partial` to fix values for one or more arguments of the original function.<jupyter_code>from functools import partial
def line_function(x, slope, intercept):
    return slope*x + intercept
line1 = partial(line_function, slope=2.0, intercept=3.0)
line1(0.0), line1(1.0)<jupyter_output><empty_output><jupyter_text>More generally, we can define a closure, i.e., a function that returns another function.<jupyter_code>def create_line(slope, intercept):
    def line(x):
        return slope*x + intercept
    return line
line2 = create_line(2.0, 3.0)
line2(0.0), line2(1.0)<jupyter_output><empty_output><jupyter_text>Coroutines Coroutines are special functions that have at least one, but possibly multiple entry points for suspending and resuming execution, while retaining state between suspend and resume. In the function below, the function's execution will be suspended at the `yield` expression, and will be resumed when the funciton is called with the number of the next floor for the elevator. This is accomplished using the `send` method defined on coroutine functions.<jupyter_code>def create_elevator(highest_floor):
    def elevator():
        floor = 0
        while True:
            next_floor = (yield f'current floor: {floor}')
            if next_floor is not None:
                if 0 <= next_floor <= highest_floor:
                    floor = next_floor
                else:
                    print(f'no such floor: {next_floor}')
            else:
                break
    my_elevator = elevator()
    next(my_elevator)
    return my_elevator    
my_elevator = create_elevator(2)
for floor in random.choices([-1, 0, 1, 2, 3], k=10):
    print(f'press {floor}: {my_elevator.send(floor)}')<jupyter_output>press 0: current floor: 0
press 1: current floor: 1
press 2: current floor: 2
no such floor: 3
press 3: current floor: 2
no such floor: -1
press -1: current floor: 2
no such floor: -1
press -1: current floor: 2
press 1: current floor: 1
press 1: current floor: 1
no such floor: 3
press 3: current floor: 1
press 1: current floor: 1
{'fraction_non_alphanumeric': 0.09566596194503171, 'fraction_numerical': 0.028012684989429177, 'mean_word_length': 4.278940027894003, 'pattern_counts': {'":': 0, '<': 32, '<?xml version=': 0, '>': 30, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '8', 'dir': 'jupyter-structured-clean-dedup', 'id': '449887', 'license': 'CC-BY-4.0', 'n_tokens_mistral': 1274, 'n_tokens_neox': 1183, 'n_words': 463, 'path': 'Python/Functional/functional_programming_style.ipynb', 'repo_name': 'Gjacquenot/training-material', 'size': '3784'}