Pin-Jiun / Programming-Language-CPP

It's the note/experience about C++, and I have the basic ability of C.
0 stars 0 forks source link

20-Lambda Expressions or Anonymous Function #27

Open Pin-Jiun opened 1 year ago

Pin-Jiun commented 1 year ago

Lambda Expressions, Anonymous Function

匿名函數,顧名思義就是沒有名稱的函數 可以用表達式來定義函數,這樣使得函數的定義和調用在一起,語意和邏輯上更為緊湊 對於只是用一次的短小的函數,直接調用匿名的lambda表達式是最好的選擇,這樣就不需要給每個函數起名字了 Lambda expression 基本的用法如下:

[capture clause] (parameter list) {...function...}
[lambda-introducer] (lambda declarator) mutable throw() -> return type {function}
#include <iostream>
using namespace std;

auto lambda = [](void) -> void { cout << "Hello, Lambda" << endl; };
//將HelloLambda這個沒有名字的function傳回給lambda

int main() {
    lambda();   //呼叫lambda()
}

由於這裡的 lambda expression 並沒有需要傳入任何參數,所以可以連同小括號一起省略,改寫成這樣:

auto lambda = [] { cout << "Hello, Lambda" << endl; };

也可以在參數列加上 void,明確標示沒有傳入參數:

auto lambda = [](void) { cout << "Hello, Lambda" << endl; };

化簡後如下

auto lambda = []() { cout << "Hello, Lambda" << endl; };

[ ] lambda-introducer and capture clause

lambda-introducer,也稱為 capture clause。

lambda expression 開頭的關鍵字,不可以省略

抓取(capture)變數的功能,指定該如何將目前 scope 範圍之變數抓取至 lambda expression 中使用 抓取變數的方式則分為傳值(by value)與傳參考(by reference)兩種 []:只有兩個中括號,完全不抓取外部的變數。 [=]:所有的變數都以傳值(by value)的方式抓取。 [&]:所有的變數都以傳參考(by reference)的方式抓取。 [x, &y]:x 變數使用傳值、y 變數使用傳參考。 [=, &y]:除了 y 變數使用傳參考之外,其餘的變數皆使用傳值的方式。 [&, x]:除了 x 變數使用傳值之外,其餘的變數皆使用傳參考的方式。 這裡要注意一點,預設的抓取選項(capture-default,亦即 = 或是 &)要放在所有的項目之前,也就是放在第一個位置。


( ):lambda declarator and parameter list

定義此匿名函數的傳入參數列表,基本的用法跟一般函數的傳入參數列表一樣,不過多了一些限制條件:

參數清單在 lambda expression 中並不是一個必要的項目,如果不需要傳入任何參數的話,可以連同小括號都一起省略。


mutable:mutable specification 加入此關鍵字可以讓 lambda expression 直接修改以傳值方式抓取進來的外部變數,若不需要此功能,則可以將其省略。

throw():例外狀況規格(exception specification)。 指定該函數會丟出的例外,其使用的方法跟一班函數的例外指定方式相同。如果該函數沒有使用到例外的功能,則可以直接省略掉。


->return type:傳回值型別

指定 lambda expression 傳回值的型別,這個範例是指定傳回值型別為整數(int),其他的型別則以此類推。如果 lambda expression 所定義的函數很單純,只有包含一個傳回陳述式(statement)或是根本沒有傳回值的話,這部分就可以直接省略,讓編譯器自行判斷傳回值的型別。


配合 STL 使用

C++ 標準程式庫中有許多的函數在使用時會需要其他的函數作為傳入參數,最常見的就是一些對於陣列的處理函數,這個例子是 std::count_if 最簡單的使用方式:

#include <iostream>
#include <algorithm>
#include <vector>
using namespace std;
// 提供給 std::count_if 用的函數
bool condition(int value) {
  return (value > 5);
}
int main() {
  vector<int> numbers { 1, 2, 3, 4, 5, 10, 15, 20, 25, 35, 45, 50 };
  // 看看 numbers 中有幾個元素符合 condition 函數所定義的判斷條件
  auto count = count_if(numbers.begin(), numbers.end(), condition);
  cout << "Count: " << count << endl;
}

這裡我們定義一個 condition 函數,作為std::count_if 在判斷元素時的依據 std::count_if 會將每個元素一一傳入condition 函數中檢查,最後傳回所有符合條件的元素個數。

由於 std::count_if 所使用到的判斷函數都需要另外定義,這樣會讓程式碼顯得很冗長,我們可以使用 lambda expression 改寫一下,讓整個程式碼更簡潔:

#include <iostream>
#include <algorithm>
#include <vector>
using namespace std;
int main() {
  vector<int> numbers { 1, 2, 3, 4, 5, 10, 15, 20, 25, 35, 45, 50 };
  // 使用 lambda expression 替代原有的 condition 函數
  auto count = count_if(numbers.begin(), numbers.end(),
    [](int x) { return (x > 5); });
  cout << "Count: " << count << endl;
}

我們將原本condition 函數所在的位置,直接使用一個 lambda expression 替換 至於傳入的參數與傳回值的類型則維持不變(傳入 int,傳回 bool)

參考資料 https://www.itread01.com/articles/1486257557.html https://blog.gtwang.org/programming/lambda-expression-in-c11/