# Определение функция высшего порядка. Замыкания. Декораторы. 1. Написать функцию `combine`, принимающую два аргумента: функции f и g и возвращающая новую функцию f * g ```Python x = 42 f = lambda x: x // 7 g = lambda x: x ** 2 assert g(f(x)) == combine(f, g)(x) ``` 2. Написать функцию `curry`, которая в качестве аргумента принимает функцию с неопределенным количеством **позиционных** (не меньше одного) аргументов и возвращает функцию одного аргумента, которая возвращает функцию от остальных аргументов: ```Python f = lambda x, y, z: x + y + z assert curry(f)(1)(2, 3) == f(1, 2, 3) ``` 3. Написать декоратор `n_times`, в случае декорирования функции которым, она будет применена к собственному результату `n` раз (`n` - аргумент декоратора). ```Python @n_times(1) def sum_2(x): return x + 2 assert sum_2(4) == 6 @n_times(10) def sum_2(x): return x + 2 assert sum_2(4) == 24 4. Написать декоратор `series` с аргемнтом `initial` (условно, значение члена ряда с индексом 0), который оборачивает функцию с аргументами `i, prev` и превращает ее в функцию вычисления ряда, заданного рекуррентно: ```Python @series(1) def factorial(i, prev): return i * prev assert factorial(4) == [1, 2, 6, 24] ``` 5. Написать функцию `mean_closure`, котороя возвращает функцию, которая при каждом обращении возвращает среднее от всех аргументов, которые ей были переданы. (Если возникает ошибка вычислений с плавающей точкой и оператор `==` возвращает False, то можно использовать `math.isclose`) ```Python mean = mean_closure() assert mean(1) == 1.0 assert mean(3) == 2.0 assert mean(2) == 2.0 ``` 6. Написать декоратор `tag`, который оборачивает функцию произвольных аргументов, возвращающую строковое значение, обрамляя это значение выбранным html-тегом (параметр декоратора): ```Python @tag('html') @tag('p') def greet(s): return f"Hello, {s}!" assert greet("World") == "<html><p>Hello, World!</p></html>" ``` 7. Написать функцию then, которая в качестве аргумента принимает две функции f и g и возвращает новую функцию `g * f` ```Python x = 42 f = lambda x: x // 7 g = lambda x: x ** 2 assert f(g(x)) == then(f, g)(x) ``` 8. Написать декоратор `http`, который оборачивает функцию произвольных аргементов, возвращающую строковое значение, который возвращает кортеж (строковое значение, 200), если строка непуста, иначе ('Error: not found', 404) ```Python @http def dummy(s): return s assert dummy("Response") == ("Response", 200) assert dummy("") == ("Error: not found", 404) assert dummy("Error: not found") == ("Error: not found", 200) ``` 9. Написать замыкание `counter_clojure`, возвращает функцию, которая при каждом обращении подсчитывает частоту всех символов в переданных аргументах: ```Python counter = counter_clojure() assert counter("Привет") == {"П": 1, "р": 1, "и": 1, "в": 1, "e": 1, "т": 1} assert counter("мир") == {"П": 1, "р": 2, "и": 2, "в": 1, "e": 1, "т": 1, "м": 1} ``` 10. Написать декоратор `logger`, который принимает произвольную функцию, и возвращает функцию, которая до выполнения выводит все свои аргументы (именованные и неименованные в stderr), а после выполнения выводит результат в stderr. Все значения должны быть оценены как строковые ```Python @logger greet(greeting, name): return f"{greeting}, {name}!" assert greet("Hello", name="Alice") == "Hello, Alice!" # В stderr должно быть выведено две строчки # Arguments: (Hello, name=Alice) # Result: Hello, Alice ```