it-swarm-vi.com

Lambda là gì và tại sao nó lại hữu ích?

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?

55
jokoon
  • 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à Word lambda được sử dụng để tạo các hàm ẩn danh trong nhiều ngôn ngữ dựa trên LISP vì 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 sử dụng các hàm ẩn danh hoặc lập trình sử dụ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à sự khác biệt của chúng từ con trỏ hàm):

Các con trỏ hàm, đã tồn tại trong C, khá hữu ích để ví dụ: truyền một hàm so sánh cho 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ơ vectơ theo phần tử ith của mỗi vectơ (trong đó i là một 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ơ bằng phần tử ith của chúng, sẽ cần lấy ba đối số (i và 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ố i cho 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ó phương thức operator(). Bây giờ chúng ta có thể định nghĩa một lớp CompareByIthElement, lấy đối số i làm đối số hàm tạo và sau đó lấy hai vectơ được so sánh làm đối số cho phương thức operator(). Để sắp xếp một vectơ của vectơ theo phần tử ith giờ đây chúng ta có thể tạo một đối tượng CompareByIthElement với i làm đố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 loại 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á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 phần tử ith 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 logic thành chức năng của chính nó (ngoài việc bạn không thể 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 mã lambda như [x1, x2](y1,y2){bla} code đượ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à operator() với các đối số (y1y2) và cơ thể bla.
  2. Tạo một thể hiện của lớp, đặt các biến thành viên x1x2 Thành các giá trị của các biến x1x2 Hiện tại 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.

43
sepp2k

Về cơ bản, các hàm lambda là các hàm bạn tạo "đang hoạt độ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 cần some_functor Chỉ cho một cuộc gọi này tới 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 phạm vi không gian tên nào đó khiến mọi người nhìn vào mã tự hỏi nó cần gì cho
18
sbi

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])
7
Martin Konecny