Функции высшего порядка map
, filter
и reduce
— это наиболее распространенные и мощные инструменты в программировании. Они позволяют работать с коллекциями данных. Эти функции упрощают и ускоряют работу с данными, позволяют писать более чистый и эффективный код.
В этом уроке мы рассмотрим подробно каждую из этих функций и научимся применять их в практических задачах.
Map
Допустим, мы хотим применить к каждому элементу списка чисел функцию или операцию. Мы можем использовать цикл for
, чтобы пройтись по элементам списка и применить операции к каждому элементу.
Например, у нас есть список чисел. Мы хотим получить новый список, в котором каждый элемент будет возводиться в квадрат, а затем вычитаться десять. Мы можем решить эту задачу через цикл:
numbers = [1, 2, 3, 4, 5]
new_numbers = []
for num in numbers:
squared = num ** 2
subtracted = squared - 10
new_numbers.append(subtracted)
print(new_numbers)
# [-9, -6, -1, 6, 15]
У такого подхода есть недостатки: код становится громоздким, трудно читаемым и поддерживаемым.
В этом случае можно воспользоваться функцией map
, которая позволяет применить функцию к каждому элементу коллекции. Она вернет новый список с преобразованными значениями.
map
означает «отобразить». Это название пришло из математики, где так же называется функция, которая отображает одно множество значений в другое путем преобразования всех элементов с помощью трансформации. В большинстве языков также используется это имя.
Рассмотрим реализацию функции map
:
def map(func, iterable):
result = []
for item in iterable:
result.append(func(item))
return result
Этот цикл похож на цикл из примера выше. Здесь мы создаем пустой список result
и затем циклом проходимся по элементам переданного итерируемого объекта iterable
. Для каждого элемента мы вызываем функцию func
и добавляем ее результат в список result
. По завершении цикла мы возвращаем список result
.
Теперь попробуем применить функцию map
к нашей задаче:
def process_number(num):
squared = num ** 2
subtracted = squared - 10
return subtracted
numbers = [1, 2, 3, 4, 5]
new_numbers = map(process_number, numbers)
print(new_numbers)
# [-9, -6, -1, 6, 15]
В данном примере мы сначала определили функцию process_number
, которую хотим применить к каждому элементу списка. Далее передали ее со списком numbers
в функцию map
.
Функция map
позволила нам более эффективно и лаконично обработать коллекцию данных. Она применила функцию к каждому элементу коллекции и вернула новый список с преобразованными значениями.
Далее рассмотрим функцию filter
.
Filter
Часто в программировании нужно отфильтровать коллекцию данных — то есть выбрать из коллекции только те элементы, которые удовлетворяют определенным условиям.
Например, если у нас есть список чисел и мы хотим получить только те, которые больше пяти, то мы можем использовать цикл for
, чтобы пройтись по элементам списка и проверить условия для каждого элемента:
numbers = [2, 7, 1, 8, 4, 5]
result = []
for num in numbers:
if num > 5:
result.append(num)
print(result)
# [7, 8]
При этом у такого подхода тоже есть свои недостатки: код становится громоздким и трудночитаемым.
В этом случае можно воспользоваться функцией filter
. Она позволяет отфильтровать элементы коллекции на основе заданного условия и вернуть новую коллекцию с элементами, которые удовлетворяют этому условию. Многие языки имеют аналогичную функцию с тем же именем.
Реализация функции filter
аналогична реализации функции map
:
def filter(func, iterable):
result = []
for item in iterable:
if func(item):
result.append(item)
return result
Здесь мы создаем пустой список result
и затем циклом проходимся по элементам переданного итерируемого объекта iterable
. Для каждого элемента мы вызываем функцию func
и проверяем, удовлетворяет ли он заданному условию. Если условие выполняется, то мы добавляем элемент в список result
. По завершении цикла мы возвращаем список result
.
Теперь рассмотрим пример использования функции filter
. Допустим, у нас есть список чисел, и мы хотим получить только те числа, которые больше пяти:
def greater_than_five(num):
return num > 5
numbers = [2, 7, 1, 8, 4, 5]
result = filter(greater_than_five, numbers)
print(result)
# [7, 8]
Здесь мы сначала определили функцию greater_than_five
, которая возвращает True
, если переданный ей аргумент больше пяти. Далее передали ее со списком numbers
в функцию filter
.
Так функция filter
упростила код и повысила его читаемость и дальнейшее сопровождение.
Теперь рассмотрим функцию reduce
.
Reduce
Последняя функция из нашей тройки — reduce()
(говорят "свертка"), который используется для агрегации данных. Под агрегацией понимается операция, вычисляющая значение, зависящее от всего набора данных. С помощью функции reduce
можно последовательно применить операции к элементам списка, чтобы получить единственное значение. Допустим, у нас есть список чисел, и мы хотим получить их сумму. В этом случае мы можем использовать цикл for
, чтобы последовательно складывать каждый элемент списка. Например:
numbers = [1, 2, 3, 4, 5]
result = 0
for num in numbers:
result += num
print(result)
# 15
В данном примере мы объявляем переменную result
и присваиваем значение 0. Затем циклом for
проходим по всем элементам списка numbers
. Каждый элемент списка numbers
прибавляем к значению переменной result
. После завершения цикла в переменной result
у нас хранится сумма всех элементов списка numbers
.
Теперь рассмотрим пример, когда нам нужно перемножить все элементы в списке. Мы также можем решить эту задачу через цикл:
numbers = [1, 2, 3, 4, 5]
result = 1
for num in numbers:
result *= num
print(result)
# 120
Здесь у нас код похож на сложение всех элементов списка. Отличие — начальное значение переменной result
и операция, которую мы выполняем.
Теперь улучшим код для этих двух примеров. Воспользуемся функцией reduce
, которая позволяет последовательно применять операцию к элементам списка, возвращая единственное значение.
Рассмотрим реализацию функции reduce
:
def reduce(func, iterable, initial):
result = initial
for item in iterable:
result = func(result, item)
return result
В данном примере на вход функции reduce
передаются три аргумента:
func
— функция, которую мы будем применять к элементамiterable
iterable
— итерируемый объект, элементы которого мы будем обрабатывать функциейfunc
initial
— начальное значение, которое будет использоваться при первом вызове функцииfunc
Внутри функции создаем переменную result
, которой в качестве начального значения передаем значение initial
. Затем циклом for
проходим по всем элементам объекта iterable
и каждый элемент вместе со значением result
передаем в функцию func
. Результат функции func
записываем в переменную result
. Результатом работы функции reduce
будет финальное значение переменной result
.
Теперь рассмотрим пример использования функции reduce
, чтобы найти сумму и произведение элементов списка:
def add(x, y):
return x + y
def multiply(x, y):
return x * y
numbers = [1, 2, 3, 4, 5]
result = reduce(add, numbers, 0)
print(result)
# 15
result = reduce(multiply, numbers, 1)
print(result)
# 120
Здесь мы создали две функции add
и multiply
, чтобы сложить и перемножить два числа. Далее мы используем функцию reduce
, чтобы получить результат применения функций add
и multiply
к элементам списка numbers
.
При первом вызове функции reduce
мы передаем начальное значение 0, а при втором вызове — значение 1. Так нам удалось сократить и сделать более читаемый код.
Map, filter и reduce против цикла for
Мы можем реализовать любую логику в цикле for
, но этот инструмент не всегда удобный или понятный. Например, если нам нужно преобразовать каждый элемент списка, то использование цикла for
может вызвать проблемы с читаемостью кода и его поддержкой в будущем.
В этом случае мы можем воспользоваться функцией map
, которая позволяет применить функцию к каждому элементу списка и вернуть новый список с преобразованными значениями. Также для выбора элементов по определенному условию вместо цикла for
можно использовать функцию filter
, которая вернет элементы, удовлетворяющие заданному условию.
Если нам нужно произвести агрегацию значений списка, например, сложить или умножить все его элементы, то для этого необязательно использовать цикл for
. В этом случае можно воспользоваться функцией reduce
, которая последовательно применяет заданную функцию к парам элементов списка и возвращает окончательный результат.
Цикл не всегда можно заменить на функции map
, filter
или reduce
. Но если речь идет о простых манипуляциях со списками, то использование этих функций может значительно упростить чтение, написание и понимание кода. Еще это помогает избежать ошибок в логике и сделать код более читаемым и поддерживаемым в будущем, так как каждая функция делает одну работу.
Выводы
В этом уроке мы рассмотрели три функции: map
, filter
и reduce
. Это более простой и лаконичный способ выполнить операции, которые могут быть выполнены также с помощью цикла for
.
Эти функции могут быть менее мощными, чем цикл for
, и не позволяют выполнить такое разнообразное количество операций. Но они делают код более лаконичным, удобочитаемым и предсказуемым. Использование этих функций может упростить написание и понимание кода, особенно, при работе с большими объемами данных.
При этом в сложных случаях, когда нужно изменить состояние объектов или выполнять некоторые специфичные операции, цикл for
может оставаться предпочтительным. Поэтому при выборе способа решения задачи всегда нужно учитывать особенности конкретной задачи и выбирать наиболее подходящий инструмент для ее решения.
Остались вопросы? Задайте их в разделе «Обсуждение»
Вам ответят команда поддержки Хекслета или другие студенты
- Статья «Как учиться и справляться с негативными мыслями»
- Статья «Ловушки обучения»
- Статья «Сложные простые задачи по программированию»
- Вебинар «Как самостоятельно учиться»
Для полного доступа к курсу нужен базовый план
Базовый план откроет полный доступ ко всем курсам, упражнениям и урокам Хекслета, проектам и пожизненный доступ к теории пройденных уроков. Подписку можно отменить в любой момент.