天天看點

C++霧中風景8:Lambda表達式

上一篇C++的部落格是Long Long ago了,前文講到在看Lambda表達式的内容。筆者首次接觸Lambda表達式應該是學習Python語言的時候,當時也不太明白這種表達方式的精髓,後續接觸了Scala與Java8的鍊式調用與Lambda結合的方式,深陷無法自拔。是以借上一篇閉包的内容。我們來完整的梳理一下C++之中的Lambda表達式。

1.什麼是Lambda表達式?

Lambda表達式是函數式程式設計的重要的文法結構。

Lambda 表達式(lambda expression)說起來很簡單,就是一個匿名函數,即沒有函數名的函數。Lambda表達式可以表示閉包。(注意和數學傳統意義上的不同)。(本質上Lambda表達式就是将函數作為是一個匿名對象進行操作)

其實缺少Lambda表達式的程式設計語言并不會影響程式設計語言的邏輯表達,Lambda表達式核心就是提供一個好用的文法糖:可以直接定義一個函數,而不需要将定義函數和文法内容分開,這樣有助于将邏輯用更緊湊的方式表達出來。假如需要定義一個函數,恰巧這個函數僅僅使用一次,然後又需要給它定義一個名字,作為懶惰的程式員就需要搬出Lambda表達式了。咱們看一段Python代碼,過濾一個list之中的偶數,這是一個很簡單的需求,我們先看看不使用Lambda表達式的方式:

def isOdd(n):
    return n & 1;

nums = [1,2,3,4,5,6]
nums = filter(isOdd,nums)
           

顯然這裡需要額外定義一個代碼邏輯十分麻煩:首先需要跳脫出運作代碼而去檢視定義的isOdd函數的代碼,其次,這裡需要實作的過濾邏輯很簡單。這種場合是最适合使用Lambda表達式的場景,我們來看看Lambda表達式是怎麼優化上述代碼的:

nums = [1,2,3,4,5,6]
nums = filter(lambda x:x & 1,nums)
           

好吧,很優雅的用Lambda表達式解決了同樣的需求,表述也十厘清晰:

下面的幾個使用場景是适用于Lambda表達式的:

  • (1)代碼定義的邏輯與執行邏輯對接的更加緊湊。
  • (2)代碼更加簡潔。
  • (3)能夠支援閉包。

2.C++之中的Lambda表達式

C++在C++11之中添加了Lambda表達式的文法結構,Lambda文法結構如下所示:

[capture](parameters)->return-type {body}
           

接下來,我們來一一分析各個部分所代表的含義,以及具體的使用方式:

  • [capture]

    capture代表捕獲外部的變量,這個使用的方式筆者在上一篇有關閉包的内容之中就有過示例,變量捕獲是Lambda表達式之中,最為複雜的一環,我們來看一看其中各種表示方式的含義:

    • [] 不捕獲任何變量(但是必須得寫,編譯器通過捕獲結構識别Lambda表達式)
    • [&} 通過引用的方式捕獲外部作用域中所有變量
    • [=] 通過拷貝的方式捕獲外部作用域中所有變量

      (上述兩種方式都過于粗暴了,實際的話,盡量采用下面的模式來限定所引用的變量,不要随意引用)

    • [x, &y] x按值傳遞,y按引用傳遞
    • [this] 截取目前類中的this指針。如果已經使用了&或者=就預設添加此選項。

可以看到,[capture]的文法結構捕獲了外部變量,通過這樣的方式實作了閉包。

  • (parameters)

    這個部分很簡單,類似于通常函數使用的參數清單,使用方式也沒有差別。

  • ->return-type

    顯式指明由Lambda表達式所傳回的傳回值類型。這裡通常建議不寫,因為C++編譯器會通過類型推斷的方式來推斷出函數的傳回值類型,而且前面的->也可以省略。

  • {body}

    花括号括起來的函數體,則沒什麼好說的了,就是實作函數邏輯的部分了。

同樣的,我們來看看上文用python實作的過濾偶數的代碼在C++之中是如何實作的:

vector<int> nums = {1,2,3,4,5,6,7};
    vector<int> newNums(nums.size());
    
    auto last = copy_if(nums.cbegin(), nums.cend(),newNums.begin(),[](int x){return !(x & 1);});
    for_each(newNums.begin(), last, [](int x) {
        cout << x << endl;
    });
           

和python的實作相比,由于缺少了鍊式調用的方式,是以看起來C++實作的版本并沒有簡化多少應用邏輯,反而看起來略顯雜亂。但是這并不妨礙我們在适當的地方運用Lambda表達式,來優化我們的代碼結構。

3.其他語言與Lambda表達式

  • Java

    Java在Java 8的版本終于千呼萬喚始出來的Lambda表達式确實是讓個人很喜歡,個人也覺得Java 8對于Java這門語言有極其深遠的影響。我們來看看Java之中是如何實作上文的邏輯的:

public static void main(String[] args) {
        int[] nums = {1,2,3,4,5,6,7};
        IntStream.of(nums).filter((x)->{return (x & 1) == 1;}).forEach(System.out::println);
    }
           

相比C++而言優雅了很多,而且參數類型也能做到類型推斷,對程式員來說确實更加友好了。

坦白說:Java是一門很幸運的語言,更上了移動開發,大資料的浪潮。不過随着Google與Oracle的官司,不知道Java未來是否還能繼續現在的強勢地位。

  • Golang

    沒有Lambda表達式,咱們要的是簡潔明晰,不要學Geek那一套玩意。

4.小結

感覺本文幹貨有點略少,吐槽略多,見諒哈~~~。關于C++之中的Lambda表達式就和大家聊到這裡,希望大家在實際Coding之中可以用好它,來盡量簡潔化自己的代碼結構。