Что такое функциональное программирование?
Функциональные языки программирования разрабатываются специально для создания приложений, ориентированных на обработку списков или символьные вычисления, так как функциональное программирование основывается на использовании математических функций. Для функционального программирования неплохо подходят такие языки, как Lisp, Python, Erlang, Haskell, Clojure и многие другие.
В последнее время функциональное программирование набирает популярность: многие традиционно императивные языки, такие как Java и Python, обеспечили поддержку его техник. Данная статья поможет понять и запомнить некоторые из техник функционального программирования.
Предполагается, что у вас уже есть базовое понимание принципов функционального программирования.
Функции первого класса
В языке программирования Python функции — это объекты первого класса, поэтому для них доступны такие операции:
- Присваивать объекты функций переменным в качестве значений.
- Используя аргументы, передавать объекты функций другим функциям в качестве входных данных.
- Хранить функции внутри структур данных, таких как словари.
- Использовать функции в качестве значений, возвращаемых от других функций.
В приведенном ниже примере функция Show
присваивается переменной Tell
в качестве значения. Это присвоение не вызывает функцию для исполнения, потому что подразумевается именно объект функции, на который ссылается идентификатор Show
:
def Show(Text):
return Text.upper()
print(Show("Hello World"))
Tell = Show
print(Tell("Hello World"))
OUTPUT
HELLO WORLD
HELLO WORLD
Поскольку функции являются объектами, мы можем присвоить функцию Show
любой переменной, а затем вызвать эту переменную для обращения к функции.
Функции внутри структур данных
Функции могут храниться внутри структур данных, точно так же, как и все другие объекты языка Python. Например, можно создать словарь с ключами класса int
, а значениями — класса function
. Подобный словарь может пригодиться в том случае, когда ключ типа данных int
отображает суть хранимой в качестве значения процедуры.
# СОЗДАНИЕ СЛОВАРЯ
dict = {
0: func1,
1: func2
}
x = input() # ПОЛУЧЕНИЕ ЦЕЛОГО ЧИСЛА ОТ ПОЛЬЗОВАТЕЛЯ
dict[x]() # ВЫЗОВ ФУНКЦИИ, ПОЛУЧЕННОЙ ПО КЛЮЧУ ИЗ СЛОВАРЯ
Функции как аргументы или возвращаемые значения
Функциям можно передавать множество аргументов: объекты и переменные (одних и тех же типов данных, либо различных), а также другие функции. Последнее доступно потому, что функции могут использоваться в качестве аргументов, либо сами могут возвращать значения функций. Такие функции, которые используют в качестве входных или выходных данных другие функции, называются функциями высшего порядка — это важная часть функционального программирования, так как функции высшего порядка чрезвычайно мощны на практике.
Рассмотрим пример
Допустим, необходимо перебрать список элементов, последовательно выводя их значения на экран — используем функцию iterate
:
def iterate(list_of_items):
for item in list_of_items:
print(item)
С первого взгляда может показаться, что реализация хороша, но вот уровень абстракции реализован только один. А что, если программе необходимо не только выводить значения на экран, но и совершать иные действия во время перебора списка?
Вот и пригодятся функции высшего порядка. Создадим функцию iterate_custom
, которая принимает в качестве параметров итерируемый список и применяемую к его элементам функцию:
def iterate_custom(list_of_items, custom_func):
for item in list_of_items:
custom_func(item)
Хотя такая программа может показаться тривиальной — это невероятно мощное средство.
Мы добавили еще один уровень абстракции, сделав код более пригодным для повторного использования. Теперь можно вызывать функцию не только для печати списка, но и для любых действий, связанных с итерацией.
Чтобы упростить задачу, можно прибегнуть к возврату функций. Использование функции в качестве потока управления выбором следующей подходящей функции похоже на хранение функций в качестве значений словаря, например:
def add(x, y):
return x + ydef sub(x, y):
return x - ydef mult(x, y):
return x * ydef calculator(opcode):
if opcode == 1:
return add
elif opcode == 2:
return sub
else:
return mult my_calc = calculator(2) #MY CALC IS A SUBSTRACOTR
my_calc(5, 4) #RETURNS 5 - 4 = 1
my_calc = calculator(9) #MY CALC IS A MULTIPLLIER
my_calc(5, 4) #returns 5 x 4 = 20.
Вложенные функции
Можно определить функцию внутри другой функции, создав таким образом внутреннюю функцию. Они особенно полезны в качестве вспомогательных функций — небольших многоразовых функций, подмодулей для поддержки основной функции.
Вспомогательные функции удобны, когда проблема требует решения с помощью специфического определения функции (тип или порядок аргументов) но легче решить проблему без соблюдения соглашения.
Допустим, необходимо определить функцию fib(n)
, возвращающую следующее число из последовательности Фибоначчи — nth
, с одним аргументом — n
.
Один из возможных способов определения такой функции — использование вспомогательной функции, которая отслеживает два предыдущих члена последовательности Фибоначчи (поскольку число Фибоначчи — это сумма двух предыдущих чисел Фибоначчи):
def fib(n):
def fib_helper(fk1, fk, k):
if n == k:
return fk
else:
return fib_helper(fk, fk1+fk, k+1) if n <= 1:
return n
else:
return fib_helper(0, 1, 1)
Перенос вычислений из тела функции в аргумент функции — это невероятно мощный прием, поскольку он сокращает избыточные вычисления из рекурсивного подхода.
Лямбда-выражения (функции одного выражения)
Как определить функцию, не указывая для неё идентификатор? Для этого созданы лямбда-функции, позволяющие определять короткие и линейные функции особым образом:
add = lambda x, y: x + y
add(1, 2) # ВОЗВРАЩАЕТ 2
Поведение функции из переменной add
здесь точно такое же, как и поведение, определенное ранее с помощью традиционного ключевого слова def
.
Обратите внимание, что лямбда-функции должны записываться в одну строку и не должны содержать явно указанный программистом оператор возврата return
.
На самом деле, лямбда-функции всегда содержат неявный оператор возврата (в примере выше return
возвращает результат операции x + y), но явные операторы возврата в лямбда-функциях отсутствуют.
Лямбда-функция намного мощнее и лаконичнее, потому что она позволяет создавать анонимные функции — функции без имени:
(lambda x, y: x * y)(9, 10) # ВОЗВРАЩАЕТ 90
Лямбда-функция — это удобный метод для тех случаев, когда функция нужна только один раз и в дальнейшем её не нужно использовать. Например, при заполнении словаря:
import collections
pre_fill = collections.defaultdict(lambda: (0, 0))
# Все значения и ключи словаря установлены в целое число 0
Теперь настало время рассмотреть Map, Filter и Reduce, чтобы еще лучше оценить полезность лямбда-функций.
Map, Filter и Reduce
1. Map
map
— это функция, принимающая на вход набор данных и преобразующая их в другой набор на основе заданной функции, то есть делающая то же самое, что и функция iterate_custom
. Например:
def substract_1(x):
return x - 1
scores = [10, 9, 8, 7, 6, 5]
new_scores = list(map(substract_1, scores))
# scores ТЕПЕРЬ РАВНЯЕТСЯ [9, 8, 7, 6, 5, 4]
print(new_scores)
В Python 3 функция map
возвращает объект класса map
, который можно преобразовать в список для дальнейшего использования. Теперь, вместо явного определения функции multiply_by_four
, можно определить лямбда-выражение:
new_scores = list(map(lambda x: X-4, scores))
2. Filter
Функция filter, как следует из названия, помогает “отфильтровать” нежелательные элементы из последовательности. Например, с её помощью можно легко и быстро отфильтровать все нечетные числа из списка scores
:
even_scores = list(filter(lambda x: (x % 2 == 0), scores))
#even_scores = [6, 8]
Поскольку функция, предоставляемая для фильтрации, решает вопрос о принятии элемента в новую последовательность в каждом из конкретных случаев, то она должна возвращать значение bool
(как показано в лямбда-функции выше) и быть унарной (принимать один входной параметр).
3. Reduce
Функция reduce
полезна для “обобщения”, получения общей картины набора данных. Например, если необходимо вычислить сумму всех оценок, то reduce
прекрасно с этим справится:
sum_scores = reduce((lambda x, y: x + y), scores)
# sum_scores = 32
Это гораздо проще, чем писать цикл. Обратите внимание, что функции reduce
требуется предоставить два параметра: один представляет текущий проверяемый элемент последовательности, а другой — суммарный результат применяемой операции.
Все вышесказанное поможет основательно начать знакомство с функциональным программированием в Python.
Читайте также:
- 3 инструмента для отслеживания и визуализации выполнения кода на Python
- Как оптимизировать набор текста с помощью Python
- Как создать простого командного бота в Python
Читайте нас в Telegram, VK и Яндекс.Дзен
Оригинал статьи: Function Programming with Python by Neel Gorasiya