Lambda là gì và tại sao nó lại hữu ích? [đóng cửa]


56

Cho đến nay tôi đã nghe nói về:

  • Giải tích Lambda
  • Lập trình Lambda
  • Biểu thức Lambda
  • Hàm Lambda

Mà tất cả dường như có liên quan đến lập trình chức năng ...

Rõ ràng nó sẽ được tích hợp vào C ++ 1x, vì vậy tôi có thể hiểu rõ hơn về nó ngay bây giờ:

http://en.wikipedia.org/wiki/C%2B%2B0x#Lambda_fifts_and_expressions

Ai đó có thể định nghĩa ngắn gọn những thứ lambdas là gì và đưa ra một nơi có thể hữu ích?


2
Lưu ý rằng thuật ngữ trong lịch sử xuất phát từ việc muốn một cách tốt để nói về chức năng được biểu thị bằng một biểu thức. Hàm được đại diện bởi x + 1 sau đó được viết lambda x. x + 1.
kasterma

Theo cách lambda, một hàm ăn một hàm khác và / hoặc một giá trị đầu vào, tạo ra một hàm khác. Điều này tiếp tục cho đến khi một chức năng tạo ra một giải pháp. Ngoài ra, trứng cá sấu .
SD

Tất cả đều là trò bịp đối với tôi. Tôi nghĩ rằng tôi sẽ có một con quay hồi chuyển, tôi thích thịt cừu, duh.

Câu trả lời:


44
  • Giải tích Lambda

Tính toán lambda là một mô hình tính toán được phát minh bởi Alonzo Church trong những năm 30. Cú pháp và ngữ nghĩa của hầu hết các ngôn ngữ lập trình chức năng được lấy cảm hứng trực tiếp hoặc gián tiếp từ phép tính lambda.

Phép tính lambda ở dạng cơ bản nhất có hai thao tác: Trừu tượng (tạo hàm (ẩn danh)) và ứng dụng (áp dụng hàm). Sự trừu tượng hóa được thực hiện bằng cách sử dụng toán tử, đưa ra phép tính lambda tên của nó.

  • Biểu thức Lambda
  • Hàm Lambda

Các hàm ẩn danh thường được gọi là "lambdas", "hàm lambda" hoặc "biểu thức lambda" bởi vì, như tôi đã nói ở trên, là biểu tượng để tạo các hàm ẩn danh trong phép tính lambda (và từ lambdanày được sử dụng để tạo các hàm ẩn danh trong nhiều ngôn ngữ ngôn ngữ dựa trên cùng một lý do).

  • Lập trình Lambda

Đây không phải là một thuật ngữ thường được sử dụng, nhưng tôi cho rằng nó có nghĩa là lập trình bằng cách sử dụng các hàm ẩn danh hoặc lập trình bằng các hàm bậc cao hơn.


Thêm một chút thông tin về lambdas trong C ++ 0x, động lực của chúng và cách chúng liên quan đến con trỏ chức năng (rất nhiều điều này có thể là sự lặp lại của những gì bạn đã biết, nhưng tôi hy vọng nó giúp giải thích động lực của lambdas và chúng khác nhau như thế nào từ con trỏ hàm):

Các con trỏ hàm, đã tồn tại trong C, khá hữu ích để chuyển một hàm so sánh sang một hàm sắp xếp. Tuy nhiên, có những giới hạn cho tính hữu dụng của chúng:

Ví dụ: nếu bạn muốn sắp xếp một vectơ của vectơ theo iphần tử thứ của mỗi vectơ (trong đó ilà tham số thời gian chạy), bạn không thể giải quyết điều này bằng một con trỏ hàm. Một hàm so sánh hai vectơ theo iphần tử thứ của chúng , sẽ cần lấy ba đối số ( ivà hai vectơ), nhưng hàm sắp xếp sẽ cần một hàm lấy hai đối số. Những gì chúng ta cần là một cách nào đó để cung cấp đối số icho hàm trước khi chuyển nó sang hàm sắp xếp, nhưng chúng ta không thể làm điều này với các hàm C đơn giản.

Để giải quyết điều này, C ++ đã giới thiệu khái niệm "đối tượng hàm" hoặc "hàm xử lý". Một functor về cơ bản là một đối tượng có một operator()phương thức. Bây giờ chúng ta có thể định nghĩa một lớp CompareByIthElement, lấy đối số ilàm đối số hàm tạo và sau đó lấy hai vectơ được so sánh làm đối số cho operator()phương thức. Để sắp xếp một vectơ của vectơ theo iphần tử th, bây giờ chúng ta có thể tạo một CompareByIthElementđối tượng với itư cách là một đối số và sau đó chuyển đối tượng đó sang hàm sắp xếp.

Vì các đối tượng hàm chỉ là các đối tượng và không phải là các hàm kỹ thuật (mặc dù chúng có nghĩa là hoạt động giống như chúng), bạn không thể tạo một con trỏ hàm trỏ đến một đối tượng hàm (tất nhiên bạn có thể có một con trỏ tới một đối tượng hàm, nhưng nó sẽ có một kiểu như CompareByIthElement*và do đó không phải là một con trỏ hàm).

Hầu hết các hàm trong thư viện chuẩn C ++, lấy các hàm làm đối số được xác định bằng cách sử dụng các mẫu để chúng hoạt động với các con trỏ hàm cũng như các đối tượng hàm.

Bây giờ đến lambdas:

Việc xác định cả một lớp để so sánh bởi iphần tử thứ là một chút dài dòng nếu bạn chỉ sử dụng nó một lần để sắp xếp một vectơ. Ngay cả trong trường hợp bạn chỉ cần một con trỏ hàm, việc xác định hàm được đặt tên là tối ưu nếu nó chỉ được sử dụng một lần vì a) nó gây ô nhiễm không gian tên và b) hàm thường sẽ rất nhỏ và thực sự không có một lý do chính đáng để trừu tượng hóa logic thành chức năng của chính nó (ngoài việc bạn không thể có các con trỏ hàm mà không xác định hàm).

Vì vậy, để sửa chữa lambdas này đã được giới thiệu. Lambdas là các đối tượng chức năng, không phải con trỏ hàm. Nếu bạn sử dụng [x1, x2](y1,y2){bla}mã lambda giống như mã được tạo, về cơ bản sẽ thực hiện như sau:

  1. Xác định một lớp có hai biến thành viên ( x1x2) và một operator()với các đối số ( y1y2) và phần thân bla.
  2. Tạo một thể hiện của lớp, đặt các biến thành viên x1x2các giá trị của các biến x1x2hiện trong phạm vi.

Vì vậy, lambdas hoạt động giống như các đối tượng chức năng, ngoại trừ việc bạn không thể truy cập vào lớp được tạo để thực hiện lambda theo bất kỳ cách nào khác ngoài việc sử dụng lambda. Do đó, bất kỳ hàm nào chấp nhận hàm functor làm đối số (về cơ bản có nghĩa là bất kỳ hàm không C nào trong thư viện chuẩn), sẽ chấp nhận lambdas, nhưng bất kỳ hàm nào chỉ chấp nhận con trỏ hàm sẽ không.


Là các chức năng ẩn danh được sử dụng với con trỏ chức năng? Nếu không có gì khác biệt?
jokoon

1
@jokoon: Không, các hàm ẩn danh không thể được chuyển làm tham số cho các hàm chỉ lấy con trỏ hàm. Tuy nhiên, hầu hết các hàm lấy các hàm làm đối số được xác định bằng các mẫu để chúng có thể lấy bất kỳ loại đối tượng hàm nào làm đối số, không chỉ là con trỏ hàm. Điều đó có nghĩa là ở hầu hết các nơi bạn có thể sử dụng các con trỏ hàm ( std::sortví dụ), bạn sẽ có thể sử dụng các hàm ẩn danh thay thế. Tuy nhiên, khi xác định một hàm nên lấy một hàm ẩn danh làm đối số của nó, bạn cần sử dụng một mẫu hoặc sử dụng std::functionlàm kiểu của đối số.
sepp2k

vì vậy một con trỏ hàm không thể chứa lambda ...
jokoon

1
+1 Giải thích tuyệt vời - Tôi cũng không thể tìm ra điều đó.
Michael K

2
Tôi với Michael về điều này. Điều này rất toàn diện. +1từ tôi.
sbi

18

Về cơ bản, các hàm lambda là các hàm bạn tạo "nhanh chóng". Trong C ++ 1x, chúng có thể được sử dụng để cải thiện khả năng hỗ trợ cho lập trình chức năng:

std::for_each( begin, end, [](int i){std::cout << i << '\n';} );

Điều này sẽ gần như dẫn đến mã tương tự như mã này:

struct some_functor {
  void operator()(int i) {std::cout << i << '\n';}
};

std::for_each( begin, end, some_functor() );

Nếu bạn some_functorchỉ cần một cuộc gọi này std::for_each(), thì hàm lambda đó có một số lợi thế so với nó:

  • những gì được thực hiện trong vòng lặp được chỉ định ngay trong đó chức năng lặp được gọi
  • nó giải phóng bạn khỏi việc viết một số mã nồi hơi
  • không có functor nằm xung quanh ở một số phạm vi không gian tên khiến mọi người nhìn vào mã tự hỏi nó cần gì cho

7

Hàm lambda là tên gọi khác của hàm ẩn danh - về cơ bản là hàm không có tên.

Thông thường bạn sử dụng điều này trong các ngôn ngữ mà bạn sẽ chỉ cần sử dụng chức năng một lần. Ví dụ thay vì

def add(a, b)
  return a+b

và sau đó chuyển chức năng đó sang chức năng khác như vậy

reduce(add, [5,3,2])

Với lambda bạn chỉ cần làm

reduce(lambda x, y: a+b, [5,3,2])
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.