Термином "лямбда-функции" называют разные вещи:
- функции в лямбда-исчислении;
- любые функции первого класса в языке программирования, т.е. функции, которые можно хранить в переменных, работать с ними как со значениями;
- замыкания как частный случай функций первого класса.
Функции первого класса позволяют строить алгоритмы на основе их типов (или количеств аргументов), абстрагируясь от конкретных действий, которые в этих функциях совершаются, т.е. функции над функциями, функции высшего порядка. Примеры функций высшего порядка: map, reduce (foldl, foldr), filter. В подобных функциях полезно, чтобы функции-аргументы не имели побочных эффектов и не зависели от внешнего состояния, т.е. не были замыканиями (внешние константы не в счёт).
Функции обратного вызова (callback) — тоже функции первого класса, в которых обычно используются (
замкнуты внутрь) ссылки на переменные лексического окружения, ведь их основное назначение — производить некоторый побочный эффект. В функциональных языках это полноценные замыкания.
Внешние переменные у замыканий продолжают существовать, пока существует замыкание. Это отличается от того, как ссылки на переменные работают в Си и большинстве других языков без автоматического управления памятью.
function getCounter() {
var i = 0;
return function() {
i++;
return i;
}
}
let a = getCounter();
let b = getCounter();
a(); // 1
a(); // 2
a(); // 3
b(); // 1
b(); // 2
a(); // 4
a(); // 5
a(); // 6
Как видно в этом примере на JS, замыкания могут использоваться еще и для хранения инкапсулированного состояния, аналогично приватным атрибутам в ООП.
Несколько замыканий могут также использовать общее состояние.
function getPair() {
let state = 0;
let getter = () => state;
let setter = x => state = x;
return [getter, setter];
}
let thePair = getPair();
let read = thePair[0];
let write = thePair[1];
read(); // 0
write(123);
read(); // 123
Здесь два замыкания ссылаются на локальную переменную state. В самом предельном случае, замыкания могут ссылаться на глобальные переменные. С точки зрения замыканий, это одно и то же.
Теперь, раз уж вопрос в теме Data Science, перейдём к Python.
В Python обычные функции, объявленные через def, это функции первого класса. Они могут использовать аргументы функций, в которых объявлены (в некоторых источниках это называют термином "лексическое замыкание"). Для использования ссылок на другие свободные переменные используется ключевой слово nonlocal. Можно сказать, что ключевое слово nonlocal в Python отвечает за то, чтобы превратить функцию в полноценное замыкание.
def get_counter():
i = 0
def x():
nonlocal i
i += 1
return i
return x
a = get_counter()
b = get_counter()
a() # 1
a() # 2
b() # 1
Ключевое слово lambda напротив используется для определения коротких функций без побочных эффектов, которые ближе к тем, что определены в чистом лямбда-исчислении.
Они нужны, чтобы функции высшего порядка работали более предсказуемо.