3.3 Встроенные функции высшего порядка

3.3 Встроенные функции высшего порядка#

В числе встроенных функций также нельзя не отметить функции высшего порядка. Функцией высшего порядка мы будем называть такую функцию, которая в качестве одного из аргументов принимает другую функцию. Во многом это функции, без которых можно и обойтись при решении своих задач, но тем не менее они могут сильно упростить решение. В контексте этого приведем примеры использования двух таких функций в сравнении с кодом, написанным без их использования.

Функция map#

map(func, seq) - применяет функцию func для каждого элемента из последовательности seq

Пример задачи:

Пользователь ввёл ряд чисел с клавиатуры, для дальнейшей работы нужно преобразовать их к правильному типу.

Исходные данные и результат:

numbers = '1, 2, 3, 4, 5'  # то, что мы будем использовать как ввод
example_result = [1, 2, 3, 4, 5]  # то, что мы ожидаем получить в результате
# Стандартное решение:
split_numbers = numbers.split(', ')  # разбиваем строку по разделителю
result_1 = []
for number in split_numbers:  # перебираем все элементы и преобразуем их к числу
    result_1.append(int(number))

print(result_1)
print(result_1 == example_result)  # проверяем, что в процессе получили такой же список чисел
[1, 2, 3, 4, 5]
True

Решение той же задачи с использованием map будет выглядеть следующим образом:

# Решение через map:
split_numbers = numbers.split(', ')
result_generator = map(int, split_numbers)  # Для каждого элемента в split_numbers выполняем функцию int
result_2 = list(result_generator)  # функция map возвращает объект генератора, чтобы увидеть результат
                                   # преобразуем генератор к списку

print(result_2)
print(result_2 == example_result)
[1, 2, 3, 4, 5]
True

Функция filter#

filter(func, seq) - фильтрует последовательность seq на основе функции func, которая должна возвращать значения True/False

Пример задачи:

Пользователь что-то ввёл с клавиатуры, из введённого текса нужно отобрать только числа.

Исходные данные и результат:

sequence = '1, a, 2, b, c, 3, e, 4, 5'  # то, что мы будем использовать как ввод
example_result = ['1', '2', '3', '4', '5']  # то, что мы ожидаем получить в результате
# Стандартное решение:
split_sequence = sequence.split(', ')
result_1 = []
for element in split_sequence:  # перебираем все текстовые элементы и проверяем, состоят ли они только из цифр
    if element.isdigit():
        result_1.append(element)

print(result_1)
print(result_1 == example_result)
['1', '2', '3', '4', '5']
True

Решение той же задачи с использованием filter будет выглядеть следующим образом:

# Решение через filter
split_sequence = sequence.split(', ')
result_generator = filter(str.isdigit, split_sequence)
result_2 = list(result_generator)  # функция filter также возвращает объект генератора

print(result_2)
print(result_2 == example_result)
['1', '2', '3', '4', '5']
True

Здесь же отметим ещё две встроенные функции, которые по своей сути не являются функциями высшего порядка, но работают похожим образом - возвращают генераторы и сильно упрощают написание кода

Функция zip#

zip(seq1, seq2, ..., seqn) - принимает на вход любое количество последовательностей и в ответ генерирует кортежи из n’ых элементов. То есть сначала возвращает все первые элементы последовательностей, затем все вторые и так далее. Как только закончится любая из последовательностей, прервется и генератор zip.

Пример задачи:

Есть отдельные списки координат x, y и z, нужно получить список, где каждая координата будет представлена единым кортежем (x, y, z)

Исходные данные и результат:

x = [1, 2, 3, 4, 5]
y = [10, 20, 30, 40, 50, 60]
z = [100, 200, 300, 400, 500, 600, 700]

example_result = [(1, 10, 100), (2, 20, 200), (3, 30, 300), (4, 40, 400), (5, 50, 500)]
# Стандартное решение:
list_length = len(x)

result_1 = []
for i in range(list_length):  # Будем перебирать значения индекса в диапазоне длины списка
    coordinates = (x[i], y[i], z[i])  # Получаем нужные значения по индексу
    result_1.append(coordinates)

print(result_1)
print(result_1 == example_result)
[(1, 10, 100), (2, 20, 200), (3, 30, 300), (4, 40, 400), (5, 50, 500)]
True

Решение той же задачи с использованием zip будет выглядеть следующим образом:

# Решение через zip:
result_generator = zip(x, y, z)
result_2 = list(result_generator)

print(result_2)
print(result_2 == example_result)
[(1, 10, 100), (2, 20, 200), (3, 30, 300), (4, 40, 400), (5, 50, 500)]
True

Функция enumerate#

enumerate(seq) - принимает на вход последовательность и возвращает её элементы с их индексом (последовательным номером). Активно используется в счетных циклах.

Пример задачи:

Вывести порядковые номера для всех элементов последовательности

Исходные данные и результат:

sequence = ['a', 'b', 'c', 'd', 'e']
example_result = [(0, 'a'), (1, 'b'), (2, 'c'), (3, 'd'), (4, 'e')]
# Стандартное решение:
result_1 = []
sequence_length = len(sequence)
for i in range(sequence_length):
    result_1.append((i, sequence[i]))

print(result_1)
print(result_1 == example_result)
[(0, 'a'), (1, 'b'), (2, 'c'), (3, 'd'), (4, 'e')]
True

Решение той же задачи с использованием enumerate будет выглядеть следующим образом:

# Решение через enumerate:
result_generator = enumerate(sequence)  # в качестве второго аргумента может принимать начало отсчета
result_2 = list(result_generator)

print(result_2)
print(result_2 == example_result)
[(0, 'a'), (1, 'b'), (2, 'c'), (3, 'd'), (4, 'e')]
True

Чаще enumerate будет встречаться в следующем виде:

for i, value in enumerate(sequence):
  print(i, value)  # Дальше здесь может быть реализована любая логика
0 a
1 b
2 c
3 d
4 e