TL; DR Как указывали другие: лямбда-нотация - это просто способ определения функций без необходимости давать им имя.
Длинная версия
Я хотел бы подробнее остановиться на этой теме, потому что я нахожу это очень интересным. Отказ от ответственности: я взял курс на лямбда-исчисление давным-давно. Если кто-то с более глубокими знаниями обнаружит какие-либо неточности в моем ответе, не стесняйтесь помочь мне улучшить его.
Давайте начнем с выражений, например, 1 + 2
и x + 2
. Такие литералы, как 1
и 2
, называются константами, поскольку они связаны с конкретными фиксированными значениями.
Такой идентификатор x
называется переменным, и для его оценки вам необходимо сначала привязать его к некоторому значению. Таким образом, в основном вы не можете оценить, x + 1
пока вы не знаете, что x
есть.
Лямбда-нотация обеспечивает схему для привязки определенных входных значений к переменным. Лямбда - выражение может быть сформировано путем добавления λx .
в передней части существующего выражения, например λx . x + 1
. Переменная x
называется свободным в x + 1
и связаны вλx . x + 1
Как это помогает в оценке выражений? Если вы передаете значение лямбда-выражению, например, так
(λx . x + 1) 2
затем вы можете оценить все выражение, заменив (связав) все вхождения переменной x
значением 2:
(λx . x + 1) 2
2 + 1
3
Итак, лямбда-нотация обеспечивает общий механизм для привязки вещей к переменным, которые появляются в блоке выражения / программы. В зависимости от контекста это создает совершенно разные понятия в языках программирования:
- В чисто функциональном языке, таком как Haskell, лямбда-выражения представляют функции в математическом смысле: входное значение вводится в тело лямбда-выражения и создается выходное значение.
- Во многих языках (например, JavaScript, Python, Scheme) оценка тела лямбда-выражения может иметь побочные эффекты. В этом случае можно использовать термин « процедура» для обозначения разницы относительно чистых функций.
Помимо различий, лямбда-нотация заключается в определении формальных параметров и их привязке к фактическим параметрам.
Следующий шаг - дать имя функции / процедуре. В нескольких языках функции являются значениями, как и любые другие, поэтому вы можете дать функции имя следующим образом:
(define f (lambda (x) (+ x 1))) ;; Scheme
f = \x -> x + 1 -- Haskell
val f: (Int => Int) = x => x + 1 // Scala
var f = function(x) { return x + 1 } // JavaScript
f = lambda x: x + 1 # Python
Как отметил Эли Барзилай, эти определения просто связывают имя f
со значением, которое оказывается функцией. Таким образом, в этом отношении функции, числа, строки, символы - это все значения, которые могут быть связаны с именами одинаково:
(define n 42) ;; Scheme
n = 42 -- Haskell
val n: Int = 42 // Scala
var n = 42 // JavaScript
n = 42 # Python
В этих языках вы также можете привязать функцию к имени, используя более знакомую (но эквивалентную) запись:
(define (f x) (+ x 1)) ;; Scheme
f x = x + 1 -- Haskell
def f(x: Int): Int = x + 1 // Scala
function f(x) { return x + 1 } // JavaScript
def f(x): return x + 1 # Python
Некоторые языки, например C, поддерживают только последние обозначения для определения (именованных) функций.
Затворы
Несколько заключительных замечаний относительно замыканий . Рассмотрим выражение x + y
. Это содержит две свободные переменные. Если вы связываете, x
используя лямбда-нотацию, вы получаете:
\x -> x + y
Это не (пока) функция, потому что она все еще содержит свободную переменную y
. Вы можете сделать из него функцию, связав y
также:
\x -> \y -> x + y
или же
\x y -> x + y
который так же, как +
функция.
Но вы можете связать, скажем, y
по-другому (*):
incrementBy y = \x -> x + y
Результатом применения функции incrementBy к числу является замыкание, то есть функция / процедура, тело которой содержит свободную переменную (например y
), которая была связана со значением из среды, в которой было определено замыкание.
Так же incrementBy 5
как и функция (замыкание), которая увеличивает числа на 5.
НОТА (*)
Я немного обманываю здесь:
incrementBy y = \x -> x + y
эквивалентно
incrementBy = \y -> \x -> x + y
поэтому механизм связывания такой же. Интуитивно я думаю, что замыкание представляет часть более сложного лямбда-выражения. Когда это представление создано, некоторые из привязок материнского выражения уже были установлены, и замыкание использует их позже, когда оно будет оценено / вызвано.