TL; DR Jak zauważyli inni: notacja lambda to tylko sposób na zdefiniowanie funkcji bez konieczności nadawania im nazwy.
Długa wersja
Chciałbym trochę rozwinąć ten temat, ponieważ uważam go za bardzo interesujący. Zastrzeżenie: Dawno temu podjąłem kurs na rachunku lambda. Jeśli ktoś z lepszą wiedzą znajdzie jakieś nieścisłości w mojej odpowiedzi, nie krępuj się, pomóż mi ją poprawić.
Zacznijmy wyrażeń, np 1 + 2
i x + 2
. Literały takie jak 1
i 2
są nazywane stałymi, ponieważ są powiązane z określonymi stałymi wartościami.
Identyfikator taki jak x
nazywany jest zmienną. Aby go ocenić, najpierw musisz powiązać go z pewną wartością. Zasadniczo nie możesz oceniać, x + 1
dopóki nie wiesz, co x
jest.
Notacja lambda zapewnia schemat wiązania określonych wartości wejściowych ze zmiennymi. Ekspresji lambda może być utworzony przez dodanie λx .
przed istniejącym ekspresji, np λx . x + 1
. Zmienna x
jest uważane za darmo w x + 1
i związany wλx . x + 1
W jaki sposób pomaga to w ocenie wyrażeń? Jeśli podasz wartość do wyrażenia lambda, to tak
(λx . x + 1) 2
następnie możesz ocenić całe wyrażenie, zastępując (wiążąc) wszystkie wystąpienia zmiennej x
wartością 2:
(λx . x + 1) 2
2 + 1
3
Tak więc notacja lambda zapewnia ogólny mechanizm wiązania rzeczy ze zmiennymi pojawiającymi się w bloku wyrażenia / programu. W zależności od kontekstu tworzy to wyraźnie różne pojęcia w językach programowania:
- W czysto funkcjonalnym języku, takim jak Haskell, wyrażenia lambda reprezentują funkcje w sensie matematycznym: wartość wejściowa jest wstrzykiwana do ciała lambda i tworzona jest wartość wyjściowa.
- W wielu językach (np. JavaScript, Python, schemat) ocena treści wyrażenia lambda może mieć skutki uboczne. W tym przypadku można użyć terminu procedura, aby zaznaczyć różnicę względem funkcji czystych.
Oprócz różnic notacja lambda polega na zdefiniowaniu parametrów formalnych i powiązaniu ich z parametrami rzeczywistymi.
Następnym krokiem jest nadanie funkcji / procedurze nazwy. W kilku językach funkcje są jak wszystkie inne, więc możesz nadać funkcji następującą nazwę:
(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
Jak zauważył Eli Barzilay, te definicje po prostu wiążą nazwę f
z wartością, która okazuje się funkcją. W związku z tym funkcje, liczby, ciągi znaków, wszystkie wartości, które można przypisać do nazw w ten sam sposób:
(define n 42) ;; Scheme
n = 42 -- Haskell
val n: Int = 42 // Scala
var n = 42 // JavaScript
n = 42 # Python
W tych językach można również powiązać funkcję z nazwą, używając bardziej znanej (ale równoważnej) notacji:
(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
Niektóre języki, np. C, obsługują tylko ten ostatni zapis do definiowania (nazwanych) funkcji.
Domknięcia
Kilka uwag końcowych dotyczących zamknięć . Rozważ wyrażenie x + y
. Zawiera dwie wolne zmienne. Jeśli połączysz się x
za pomocą notacji lambda, otrzymasz:
\x -> x + y
To nie jest (jeszcze) funkcja, ponieważ nadal zawiera wolną zmienną y
. Możesz zrobić z niego funkcję, wiążąc y
również:
\x -> \y -> x + y
lub
\x y -> x + y
który jest taki sam jak +
funkcja.
Ale możesz powiązać, powiedzmy, y
w inny sposób (*):
incrementBy y = \x -> x + y
Wynikiem zastosowania funkcji incrementBy do liczby jest zamknięcie, tj. Funkcja / procedura, której treść zawiera dowolną zmienną (np. y
), Która została powiązana z wartością ze środowiska, w którym zdefiniowano zamknięcie.
Podobnie incrementBy 5
jest z funkcją (zamknięcie), która zwiększa liczby o 5.
UWAGA (*)
Trochę tu oszukuję:
incrementBy y = \x -> x + y
jest równa
incrementBy = \y -> \x -> x + y
więc mechanizm wiązania jest taki sam. Intuicyjnie myślę, że zamknięcie reprezentuje fragment bardziej złożonego wyrażenia lambda. Po utworzeniu tej reprezentacji niektóre wiązania wyrażenia macierzystego zostały już ustawione, a zamknięcie używa ich później, gdy zostanie ocenione / wywołane.