TL; DR Như những người khác đã chỉ ra: ký hiệu lambda chỉ là một cách để xác định các hàm mà không bị buộc phải đặt tên cho chúng.
Phiên bản dài
Tôi muốn giải thích một chút về chủ đề này vì tôi thấy nó rất thú vị. Tuyên bố miễn trừ trách nhiệm: Tôi đã tham gia khóa học về tính toán lambda từ lâu. Nếu ai đó có kiến thức tốt hơn tìm thấy bất kỳ sự không chính xác trong câu trả lời của tôi, vui lòng giúp tôi cải thiện nó.
Hãy bắt đầu với các biểu thức, ví dụ 1 + 2
và x + 2
. Các nghĩa đen như 1
và 2
được gọi là hằng số vì chúng bị ràng buộc với các giá trị cố định cụ thể.
Một định danh như x
được gọi là biến và để đánh giá nó, trước tiên bạn cần liên kết nó với một số giá trị. Vì vậy, về cơ bản, bạn không thể đánh giá x + 1
chừng nào bạn chưa biết nó x
là gì .
Ký hiệu lambda cung cấp một lược đồ để liên kết các giá trị đầu vào cụ thể với các biến. Một biểu thức lambda có thể được hình thành bằng cách thêm λx .
vào trước một biểu thức hiện có, ví dụ λx . x + 1
. Biến x
được cho là miễn phí trong x + 1
và ràng buộc trongλx . x + 1
Làm thế nào điều này giúp trong việc đánh giá biểu thức? Nếu bạn cung cấp một giá trị cho biểu thức lambda, như vậy
(λx . x + 1) 2
sau đó bạn có thể đánh giá toàn bộ biểu thức bằng cách thay thế (ràng buộc) tất cả các lần xuất hiện của biến x
bằng giá trị 2:
(λx . x + 1) 2
2 + 1
3
Vì vậy, ký hiệu lambda cung cấp một cơ chế chung để liên kết mọi thứ với các biến xuất hiện trong một khối biểu thức / chương trình. Tùy thuộc vào ngữ cảnh, điều này tạo ra các khái niệm khác nhau về ngôn ngữ lập trình:
- Trong một ngôn ngữ chức năng thuần túy như Haskell, các biểu thức lambda thể hiện các hàm theo nghĩa toán học: một giá trị đầu vào được đưa vào phần thân của lambda và một giá trị đầu ra được tạo ra.
- Trong nhiều ngôn ngữ (ví dụ JavaScript, Python, Scheme) đánh giá phần thân của biểu thức lambda có thể có tác dụng phụ. Trong trường hợp này, người ta có thể sử dụng thủ tục hạn để đánh dấu các hàm thuần túy wrt.
Ngoài sự khác biệt, ký hiệu lambda là về việc xác định các tham số chính thức và ràng buộc chúng với các tham số thực tế.
Bước tiếp theo, là đặt tên cho hàm / thủ tục. Trong một số ngôn ngữ, các hàm là các giá trị như bất kỳ ngôn ngữ nào khác, vì vậy bạn có thể đặt tên cho hàm như sau:
(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
Như Eli Barzilay đã chỉ ra, những định nghĩa này chỉ liên kết tên f
với một giá trị, điều này xảy ra là một hàm. Vì vậy, về mặt này, các hàm, số, chuỗi, ký tự là tất cả các giá trị có thể được liên kết với các tên theo cùng một cách:
(define n 42) ;; Scheme
n = 42 -- Haskell
val n: Int = 42 // Scala
var n = 42 // JavaScript
n = 42 # Python
Trong các ngôn ngữ này, bạn cũng có thể liên kết một chức năng với một tên bằng cách sử dụng ký hiệu quen thuộc hơn (nhưng tương đương):
(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
Một số ngôn ngữ, ví dụ C, chỉ hỗ trợ ký hiệu sau để xác định hàm (được đặt tên).
Đóng cửa
Một số quan sát cuối cùng liên quan đến việc đóng cửa . Hãy xem xét biểu thức x + y
. Điều này chứa hai biến miễn phí. Nếu bạn liên kết x
bằng cách sử dụng ký hiệu lambda, bạn sẽ nhận được:
\x -> x + y
Đây không phải là (chưa) một hàm vì nó vẫn chứa một biến miễn phí y
. Bạn cũng có thể tạo một hàm từ nó bằng cách ràng buộc y
:
\x -> \y -> x + y
hoặc là
\x y -> x + y
đó là giống như +
chức năng.
Nhưng bạn có thể liên kết, nói, y
theo một cách khác (*):
incrementBy y = \x -> x + y
Kết quả của việc áp dụng tăng hàmBy cho một số là một bao đóng, tức là một hàm / thủ tục có phần thân chứa một biến tự do (ví dụ y
) đã được liên kết với một giá trị từ môi trường trong đó đóng được xác định.
Vậy incrementBy 5
là hàm (đóng) tăng số lên 5.
GHI CHÚ (*)
Tôi đang gian lận một chút ở đây:
incrementBy y = \x -> x + y
tương đương với
incrementBy = \y -> \x -> x + y
do đó cơ chế ràng buộc là như nhau. Theo trực giác, tôi nghĩ về việc đóng cửa như thể hiện một đoạn biểu hiện lambda phức tạp hơn. Khi biểu diễn này được tạo, một số ràng buộc của biểu thức mẹ đã được đặt và bao đóng sẽ sử dụng chúng sau này khi nó được đánh giá / gọi.