亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 編程 > C++ > 正文

基于C++ Lambda表達式的程序優化

2020-01-26 14:15:01
字體:
來源:轉載
供稿:網友

什么是Lambda?

C++ 11加入了一個非常重要的特性――Lambda表達式。營里(戴維營)的兄弟都對Objective-C很熟悉,許多人多block情有獨鐘,將各種回調函數、代理通通都用它來實現。甚至有人選擇用FBKVOController、BlocksKit等開源框架將KVO、控件事件處理都改為通過block解決。原因就是簡單、方便、直觀,函數的定義和使用出現在同一個地方。這里的Lambda表達式實際上和block非常類似,當然如果你用它和Swift語言的閉包比較,那就是一回事了。

這是一個關于C/C++程序員的一個小故事,關于C++11――剛剛通過的新標準的一個小故事…

請不要誤會,題目中所提及的“優化”并不是提升程序的性能――Lambda表達式干不了這個。從本質上來說,它只是一種“語法糖”而已。不使用這種表達式,我們照樣可以寫出滿足需求的程序。正如放棄C而使用匯編,或者放棄匯編而使用機器語言一樣,你能控制的范圍就在那里,不增不減。但如果有得選擇,我相信大部分人會選擇匯編而非機器語言,選擇C而非匯編,甚至選擇C++而非C語言……。如果你確實是這樣選擇的,那么我有理由相信,你會選擇C++新標準中的Lambda表達式,因為它確實能夠簡化你的程序,讓你寫起程序來更容易;讓你的程序更易讀,更優美;同時也讓你有更多向同行炫耀的資本。

從一個實際的應用說起

讓我們還是看一個例子吧。

無論是C語言的使用者,還是C++的用戶,如果你從事PC程序的算法開發,我有96.57%的把握認為你可能使用過C++標準模板庫STL(其中的string,vector之類)。畢竟,STL的抽象不錯,不用白不用,是不是。STL中有一大類是算法,這些算法的抽象同樣不錯,我們就拿排序算法(sort)來說事吧。

假設現在有一個結構稱為Student,其中包含了ID與name兩項――分別表示學號與姓名。在某個應用中,用戶希望對一個Student的數組按照ID的從大到小排序,那么程序可能寫成如下的形式(本文中的所有程序均在Visual Studio 2010下編譯通過):

#include <string>#include <vector>#include <iostream>#include <iterator>#include <algorithm>using namespace std;struct Student {unsigned ID;string name;Student(unsigned i, string n) : ID(i), name(n){}};struct compareID {bool operator ()(const Student& val1, const Student& val2)  const {return val1.ID < val2.ID;}};int main(int argc, char* argv[]) {Student a[] = {Student(2, “John”), Student(0, “Tom”), Student(1, “Lily”)};sort(a, a+3, compareID());for(int i=0; i<3; ++i)cout<<a[i].ID<<' ‘<<a[i].name<<endl;return 0}

程序用sort進行排序,之后用一個for循環輸出結果。而之所以能完成這個排序,則是由于仿函數compardID的存在。

現在假設用戶的需求變了(或者是另一個需求),需要你按照學生的姓名進行排序,那么你需要重新寫一個仿函數如下:

struct compareName {bool operator ()(const Student& val1, const Student& val2) const {return val1.name < val2.name;}};

然后將sort的調用修改為:

sort(a, a+3, compareName());

問題出現了,你意識到了嗎?你只是想表達一個很簡單的排序方式,確不得不引入很多的代碼行來建相應的仿函數。如果這個函數在很多地方都會用到,那么建立它的價值還相對較大。如果只是用在一個地方,你也不得不中段你流暢是思路,一邊罵娘一邊寫出這么多行代碼。另一方面,程序的讀者在讀到相應部分的時候,也不得不中段他流暢的思路,在工程的某個地方苦苦求索――compareName或者compareID是怎么干的呢?

是的,是的,作為一個C++老鳥,你會說,這樣寫代碼太不專業了。完全可以有不建立仿函數的寫法,比如以ID排序時,完全可以通過引入boost庫中的bind來實現,比如這樣:

sort(a, a+3, bind(less<unsigned>(), bind(&Student::ID, _1), bind(&Student::ID, _2)));

如果你能寫出或是讀懂這段代碼,我承認你的C++水平確實說得過去(如果讀不懂,沒關系,它不是本文的重點)。但這段代碼真的好嗎?確實,這樣可以省略了仿函數。但問題是代碼的復雜性大大增加了――即使如此簡單的一個需求,bind表達式也要復雜如斯,更復雜一點的需求要寫成何等復雜的形式啊,這對于bind本身,寫程序的人,讀程序的人都是一種折磨――你hold住嗎?

如果用Lambda表達式呢,唔,這個sort語句可以這么寫:

sort(a, a+3, [](const Student& val1, const Student& val2){ return val1.ID < val2.ID; });

那個看上去有點奇怪的,sort的第三個函數就是一個Lambda表達式了。如果我們除去開頭的“[]”不看,后面的部分很像一個函數――你可以很容易地看出這個函數是干什么的:給定兩個Student元素,比較兩個元素的ID值,并返回比較結果――這玩意兒比上面那個bind結果容易閱讀多了。
事實上,利用Lambda表達式,上述程序可以修改為如下的樣子(只列出了main函數):

int main(int argc, char* argv[]) {Student a[] = {Student(2, “John”), Student(0, “Tom”), Student(1, “Lily”)};sort(a, a+3, [](const Student& val1, const Student& val2){ return val1.ID < val2.ID; });for_each(a, a+3, [](const Student& val){cout<<val.ID<<' ‘<<val.name<<endl;});return 0}

其中的for_each句用于輸出――其中的Lambda表達式意味著:對于每一個val,輸出其ID與Name值――這樣我們連for循環也省了。

Lambda表達式的引入就是為了更方便地書寫程序,更容易地閱讀程序。如同STL一樣,有什么理由不去用呢?

Lambda表達式的基本語法

有了感性的認識后,我們來分析一下Lambda表達式的語法。

我這里無意把C++標準草案中Lambda表達式的有關章節翻譯過來(我也不佩這么做)。只是在這里希望以最通俗的方式將它的語法講解一二。從結構上說,Lambda表達式可以寫成如下的形式:

Lambda-introducer lambda-declarator(opt) compound-statement

其中的Lambda-introducer就是剛剛的那個“[]”它是不能省略的。中括號中也可能出現變量。表示將局部變量傳入到Lambda表達式中。lambda-declaratoropt是可選擇的,包括了表達式的參數列表,返回值信息, mutable聲明(以及一些其它信息,這里不做討論)。而最后的compound-statement則是表達式的主要內容。

還是看一個例子吧:

int n = 10;[n](int k) mutable -> int { return k + n; };

程序的第二行是一個lambda表達式,lambda里能出現的東西幾乎全了(當然,正如我在前文說的,有一些其它信息這里不做討論,所以沒有加入其中)。讓我們對里面的東西一一分析:

  • l[n]是Lambda-introducer,而n是一個變量,表明該表達式作用域中的變量n將被傳入這個表達式。以本程序為例,傳入的值是10。Lambda-introducer可以指定變量以值的方式傳入,也可以用其它的形式指定其以引用的方式傳入。其變型大家就baidu一下吧J
  • l(int k)表示了參數列表,屬于lambda-declarator的一部分。你可以把表達式看成一個仿函數(如上文的)。這里指定了仿函數的參數列表。如果函數的參數列表為空,這一部分可以省略。
  • lmutable表示仿函數中的變量能否改變。以前文中compareID這個仿函數為例,注意到其中的operator ()是const的。如果lambda表達式中引入了這個mutable,則對應的仿函數中operator()的定義將不包含這個const――這意味著仿函數中的變量值(Lambda-introducer傳入)可以改變。討論operator() const與operator()的區別已經超出了本文的范圍,想了解的話,看看C++相關教程吧
  • l-> int 表示返回類型(這里是int)。如果編譯器能從代碼中推斷出返回類型,或者Lambda表達式的返回類型為void,則該項可省略;
  • l{ return k+n; }是compound-statement:函數體。

通過分析可以看出,這個Lambda表達式相當于一個函數,該函數讀入一個int值k,將該值加上n返回。根據上述說明,這個表達式可以簡寫為:

[n](int k){ return k + n; };

Lambda表達式可以存儲在std::function<T> 或 std:: reference_closure<T>類型的變量中。其中的T表示了表達式對應函數的類型。以上述表達式為例,它輸入參數為int型變量,輸出為int,那么為了保存它,可以寫成如下的形式:

function<int(int)> g = [n](int k){ return k + n; };

另一個例子,前文所使用的Lambda表達式:

[](const Student& val1, const Student& val2){ return val1.ID < val2.ID; }

可以存儲于function<bool(const Student&, const Student&)>這個類型的變量中。

如果你嫌這么寫麻煩,也可以利用C++新標準中另一個新特性:類型推導。即用auto作為變量的類型,讓編譯器自己推導表達式的類型:

auto g = [n](int k){ return k + n; };

沒問題,這樣寫g還是一個強類型的變量,只不過其類型是由編譯器推導的,好處是你不用寫太長的變量類型了J

Lambda表達式進階

作為結尾,我們來看一些C++ Lambda表達式進階的用法。

Lambda表達式被引入主要是用于函數式編程。有了Lambda表達式,我們也可以做一些函數式編程的東西。比如將一個函數作為返回值的應用:

auto g = [](int n) -> function<void (int)> {return [n](int k){ cout<<n+k<<' ‘; };};

它是一個Lambda表達式,輸入一個整型變量n,返回一個函數(lambda表達式),這個函數接收一個int值k,并打印出k+n。g的使用方法如下:

int a[]={1,2,3,4,5,6,7,8,9,0};function<void (int)> f = g(2);for_each(a, a+10, f);

它將輸出:3 4 5 6 7 8 9 10 11 2

有一點函數式編程的味道了

至于其它的東西,比如如下的表達式:

[](){}();

是一個有效的調用。其中“[](){}”表示一個Lambda表達式,其輸入參數為空,返回void,什么都不干。而最后的()表示調用其求值――雖然什么都不干,但編譯能通過,很唬人喔

好了,就寫到這里吧。關于Lambda表達式想說的最后一件事是:它是新標準C++11中定義的。老的編譯器不支持(這也是我用VS2010的原因)。想要用它,以及其它新標準帶來的好處嗎?嘿,你的家伙(指編譯器)該升級了。

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
欧美另类第一页| 久久久国产一区| 日韩中文字幕网址| 久久久久女教师免费一区| 精品久久久久久久久久| 日韩午夜在线视频| 久热精品视频在线免费观看| 亚洲欧洲自拍偷拍| 少妇精69xxtheporn| 国产亚洲欧洲高清一区| 国产中文欧美精品| 97碰碰碰免费色视频| 国产精品观看在线亚洲人成网| 久久久久亚洲精品国产| 亚洲在线免费观看| 国产一区二区三区日韩欧美| 亚洲毛茸茸少妇高潮呻吟| 国产91精品高潮白浆喷水| 亚洲欧美日韩网| 久久久久久成人精品| 国产精品成人观看视频国产奇米| 欧美专区中文字幕| 欧美超级免费视 在线| 国产精品久久久久一区二区| 精品偷拍各种wc美女嘘嘘| 亚洲视频在线视频| 国产网站欧美日韩免费精品在线观看| 38少妇精品导航| 成人夜晚看av| 欧美激情一区二区三区成人| 国产噜噜噜噜久久久久久久久| 国产精品久久久久久久久影视| 欧美孕妇毛茸茸xxxx| 成人免费观看49www在线观看| 久久不射电影网| 国产一区二区三区欧美| 日韩禁在线播放| 欧美伊久线香蕉线新在线| 日韩在线免费高清视频| 97超级碰在线看视频免费在线看| 国产精品v日韩精品| 成人网中文字幕| 亚洲图中文字幕| 中文字幕亚洲欧美在线| 一区二区三区久久精品| 日韩免费av片在线观看| 亚洲人成伊人成综合网久久久| 欧美激情精品久久久久久黑人| 欧美日韩在线视频一区二区| 亚洲精品国产精品自产a区红杏吧| 日韩中文字幕国产| 国外色69视频在线观看| 日本19禁啪啪免费观看www| 91色p视频在线| 久久国产色av| 91精品久久久久久久久久久久久| 国产精品自产拍在线观看中文| 最近2019中文字幕一页二页| 亚洲美女免费精品视频在线观看| 国产精品欧美激情| 日韩精品高清视频| 91精品国产乱码久久久久久久久| 欧美精品在线看| 全亚洲最色的网站在线观看| 色老头一区二区三区| 琪琪亚洲精品午夜在线| 欧美乱大交xxxxx另类电影| 成人h猎奇视频网站| 亚洲电影在线观看| 伊人精品在线观看| 国产精品激情av电影在线观看| 久久久在线观看| 欧洲中文字幕国产精品| 亚洲成人免费在线视频| 国产香蕉精品视频一区二区三区| 一色桃子一区二区| 国产精品你懂得| 日韩视频第一页| 91国产美女视频| 日韩欧美中文在线| 国产精品99导航| 国产精品美女www爽爽爽视频| 97在线免费视频| 亚洲视频综合网| 欧美精品电影在线| 成人免费在线视频网站| 久久艳片www.17c.com| 国产一区二区三区高清在线观看| 国产日韩欧美日韩大片| 欧美亚洲国产成人精品| 久久九九精品99国产精品| 欧美最猛性xxxxx(亚洲精品)| 久久色在线播放| 国产精品老女人精品视频| 日本精品视频网站| 国产一区深夜福利| 亚洲欧美精品suv| 疯狂欧美牲乱大交777| 亚洲精品按摩视频| 国产一区二区三区中文| 国产精品偷伦一区二区| 欧美性猛交xxxx富婆| 亚洲毛片在线观看| 欧美猛交免费看| 亚洲精品mp4| 欧美xxxx18性欧美| 欧美午夜性色大片在线观看| 久久国产精彩视频| 精品欧美国产一区二区三区| 日韩精品久久久久| 国产精品亚洲片夜色在线| 欧美日韩激情网| 国产精品久久久久久久久借妻| 亚洲图片在线综合| 91精品视频一区| 国产精品69精品一区二区三区| 日韩成人av网址| 国产午夜精品一区二区三区| 欧美精品中文字幕一区| 欧美激情区在线播放| 91久久久久久久一区二区| 在线丨暗呦小u女国产精品| 亚洲一二在线观看| 国产精品久久97| 日韩精品欧美国产精品忘忧草| 亚洲国产精品va在看黑人| 91国偷自产一区二区三区的观看方式| 欧美激情在线观看视频| 日韩亚洲综合在线| 91亚洲精品一区| 久久大大胆人体| 久久久99免费视频| 久久久久久久av| 久久艹在线视频| 欧美性色xo影院| 91探花福利精品国产自产在线| 亚洲国产精品国自产拍av秋霞| 久久成人精品电影| 97视频国产在线| 91成人免费观看网站| 日韩的一区二区| 国产日韩欧美综合| 欧美专区中文字幕| 狠狠综合久久av一区二区小说| 久久影院模特热| 亚洲一区二区三区sesese| 日韩激情av在线播放| 久久久久女教师免费一区| 超在线视频97| 不卡av在线播放| 亚洲欧美一区二区三区四区| 日韩av影视综合网| 69国产精品成人在线播放| 欧美性猛交xxxx黑人猛交| 4444欧美成人kkkk| 丝袜情趣国产精品| 欧美精品一区二区三区国产精品| 精品一区二区三区四区| 亚洲www永久成人夜色| 国产精品丝袜高跟| 亚洲精品资源在线| 日韩在线观看成人| 亚洲97在线观看| 国产精品久久久久免费a∨|