Функции в языках программирования — это такие небольшие автономные программы, которые работают внутри основного кода. При запуске они могут взять какие-то стартовые параметры, обработать их, получить свой результат и отдать этот результат в основной код.
Чтобы вызвать функцию, нужно указать её имя, но бывают такие функции, у которых нет имени, но их всё равно можно вызывать. Рассказываем, как это работает.
Мы будем рассказывать про лямбда-функции на примере языка Python. В других языках лямбда-функции выглядят немного иначе, но принцип работы у них точно такой же.
Как работает обычная функция
Классическая функция выглядит так:
- Имя функции.
- Тело функции.
- Может быть: результат, который она возвращает, но может и не возвращать.
Запишем это на Python, чтобы было наглядно:
def add(x, y):
result = x + y
return result
print(add(2,3))
У этой функции есть имя add
. Есть то, что она делает внутри: складывает два числа. И есть результат, который она возвращает, — он хранится в переменной result
. Чтобы вызвать эту функцию, мы указываем её имя и аргументы, которые ей нужно обработать: print(add(2,3))
.
Сейчас функция кажется избыточной, мол, сложение чисел можно сделать и так. Но саму эту функцию внутри можно сделать намного сложнее. Например, что могла бы делать эта функция:
- Проверять, какие данные ей подали на вход, и в зависимости от их типа складывать их по-разному. Например, если ей дадут две строки, она может выдать ошибку; а может соединить строки.
- Складывать не числа, а массивы из чисел. Правила сложения массивов нужно будет прописать.
- Вести учёт всех сложений, которые она делала.
- Преобразовывать входящие или исходящие данные.
- Возвращать данные не в виде числа, а в виде целого объекта с кучей свойств, если это нужно программе.
И это только банальное сложение. Всю эту логику удобно упаковать внутрь функции и вызывать с помощью простого слова add()
.
Что такое лямбда-функция
Лямбда-функция выглядит иначе: у неё нет имени, но есть ключевое слово lambda — оно показывает, что дальше пойдёт безымянная функция. В общем виде лямбда-функция выглядит так:
lambda переменные: значение функции
Чтобы эта функция могла куда-то вернуть своё значение, лямбда-функции присваивают переменным — и после этого их можно использовать как обычные функции. Перепишем наш пример со сложением в виде лямбда-функции:
result = lambda x, y: x + y
print(result(2,3))
Если мы запустим оба фрагмента кода, то в обоих случаях увидим на экране число 5.
Может показаться, что во втором случае у нас всё равно получилась функция result, просто записанная иначе, и нет никакой разницы с первым способом. Но на самом деле различия есть: обычные функции не получится использовать на месте переменных, а лямбда-функции — можно. Про это расскажем чуть ниже.
Ограничение лямбда-функций
Значение лямбда-функции в Python должно быть записано в одну строку. Это значит, что внутри лямбда-функций не может быть циклов, других функций и всего остального, что требует для записи нескольких строк.
Ещё такая функция обязательно возвращает какое-то значение — нельзя создать лямбда-функцию, которая ничего не вернёт. Смысл таких функций как раз в том, чтобы быстро что-то считать и сразу возвращать результат этих вычислений. Если нужна функция, которая ничего не возвращает, — используйте обычные, там так можно.
Также в лямбдах нет присваивания — это простое выражение, которому не нужно ничего промежуточно хранить, а нужно лишь посчитать.
Откуда такое название
Название лямбда-функций пришло в программирование из математического λ-исчисления, где λ — это как раз греческая буква «лямбда». Смысл там в том, что всё построено на переменных и их взаимодействии друг с другом. Условно, чтобы посчитать одно выражение, нужно найти значение всех его значений, тоже выраженное каким-то формулами, а потом подставить нужный параметр вместо переменной.
В лямбда-функциях всё то же самое:
- есть выражение, заданное с помощью переменных;
- есть общее значение, которое зависит от этого выражения;
- когда мы подставляем в лямбда-функцию какое-то значение, функция обрабатывает его и получает определённый результат;
- этот результат будет влиять на итог общего значения.
Зачем нужны лямбда-функции
Самая популярная область применения лямбда-функций — в качестве аргументов в других функциях. Мы с этим ещё столкнёмся в новых проектах, а пока пара примеров.
Допустим, у нас есть список с числами, из которого нам нужно отобрать только чётные. Для этого можно писать классическую функцию, которая переберёт каждый элемент списка и, если он чётный, добавит его в итоговый результат. А можно использовать лямбду и встроить фильтр сразу в момент создания нового списка:
lst = [1, 2, 3, 4, 5, 6, 7, 8, 9]
new_lst = list(filter(lambda x: (x%2 == 0) , lst))
print(new_lst)
Смотрите, что тут произошло:
- Мы сделали список с разными числами.
- Потом мы создали новый на основе старого, используя команду filter() — она выбирает из списка нужные элементы по критерию.
- В качестве такого критерия мы указали, что элемент можно брать, только если он чётный.
- Значения этих элементов берутся из старого списка lst.
Это значит, что в момент создания нового списка функция прошла по старому, нашла там все чётные элементы и добавила их в новый список. Так получилось быстрее и проще, чем городить отдельную функцию для выбора.
Вот пример посложнее, но нагляднее: вывести словарь в порядке убывания суммы каждого значения. Алгоритму нужно сначала найти сумму значений каждого элемента словаря, отсортировать их по убыванию, а потом запомнить новый порядок вывода. Для этого используют функцию sorted() — она сортирует словарь по определённому критерию, но вот сам критерий проще всего определить с лямбдой:
bigrams = {"AB": [10, 11, 12], "BC": [5, -5, 8], "CD": [105, 1, 0], "DE": [6, 6], "EF": [15, 20, 15], FG": [22, 11, 32], "GH": [20, 20, 20]}
sorter = sorted(bigrams, key=lambda key: sum(bigrams[key]), reverse=True)
for key in sorter:
print(key, bigrams[key])
Здесь key — это как раз критерий фильтра, в котором мы используем лямбда-функцию. Эта функция считает сумму значений каждого элемента словаря и превращает это в критерий сортировки. Функции остаётся только быстро пробежать по всему словарю, используя готовые суммы, и отсортировать его в обратном направлении:
А вот если бы мы сделали это в виде обычной функции, получилось бы сложнее и не так очевидно. Сравните этот код с предыдущим — он делает то же самое, но выглядит более громоздко:
from functools import partial
def sort_func(key, dict):
return sum(dict[key])
bigrams = {"AB": [10, 11, 12], "BC": [5, -5, 8], "CD": [105, 1, 0], "DE": [6, 6], "EF": [15, 20, 15], "FG": [22, 11, 32], "GH": [20, 20, 20]}
partial_sort = partial(sort_func, dict=bigrams)
sorter = sorted(bigrams.keys(), key=partial_sort, reverse=True)
for key in sorter:
print(key, bigrams[key])
Ещё лямбда-функции иногда используют для создания замыканий — функций внутри функций, которые тоже зависят от входных параметров:
def addition(x):
return lambda y: x + y
add_to_ten = addition(10)
print(add_to_ten(8))
print(add_to_ten(6))
Получается, что технически можно обойтись и без лямбда-функций, но с ними иногда получается удобнее. Это узкоспециализированный инструмент, который не нужен в каждом коде, но с ним некоторые вещи становятся гораздо проще.