1. 程式人生 > >學習C++——lambda表示式

學習C++——lambda表示式

上面三個是C++學習網站,有助於瞭解C++11的新特性。

lambda表示式(C++11)

1、介紹lambda

我們使用過的僅有兩種可呼叫物件是函式和函式指標。還有其他兩種可呼叫物件:過載了函式呼叫運算子的類,以及lambda表示式。 一個lambda表示式表示一個可呼叫的程式碼單元。我們可以將其理解為一個未命名的行內函數。 一個lambda表示式的形式為: [capture list] (parameter  list) -> return type {function body} 其中,capture list (捕獲列表)是一個lambda所在函式中定義的區域性變數的列表(通常為空); return type,parameter list , function body與任何普通函式一樣,分別表示返回型別,引數列表,函式體。但是,lambda必須使用尾置返回來指定返回型別。
尾置返回型別(C++ 11): 任何函式的定義都能使用尾置返回,但是這種形式對於返回型別比較複雜的函式最為有效,比如返回型別是陣列的指標或者陣列的引用。 尾置返回型別跟在形參列表後面並以一個->符號開頭。為了表示函式真正的返回型別跟在形參列表之後,我們在本應該出現返回型別的地方放置了一個auto。 auto  func(int  i) -> int (*) [10];//func接受一個int型別的實參,返回一個指標,該指標指向含有10個整數的陣列。 我們可以忽略引數列表和返回型別,但必須永遠包含捕獲列表和函式體: auto  f = [ ] {return 42};   定義了一個可呼叫物件f,它不接受引數,返回42;//此時的lambda沒有引數列表,沒有返回型別 ,只包括  [ ]  {return 42;}
cout << f() << endl;//這是lambda的呼叫方式,和普通函式的呼叫方式一樣,輸出42;

2、向lambda傳遞引數

呼叫一個lambda時給定的實參被用來初始化lambda的形參,與一個普通函式的呼叫是一樣的。lambda不能有預設引數。
//作為一個帶引數的lamdba的例子,我們可以編寫一個與isShorter功能一樣的lamdba
[] (const string &a, const string &b)
{ return a.size() < b.size();}
/*
	空捕獲列表表明此lamdba不使用它所在函式中的任何區域性變數。
	lamdba中的引數與isShorter類似,是const string 引用。
*/
//使用stable_sort
stable_sort(words.begin(), words.end(), 
			[] (const string &a, const string &b)
			{ return a.size() < b.size();});
//當stable_sort需要比較兩個元素時,就會呼叫這個lamdba。

3、使用捕獲列表

一個lambda只有在其捕獲列表中捕獲一個它所在函式中的區域性變數,才能在函式體中使用該變數。
[sz] (const string &a)
	{ return a.size() >= sz; };
//lamdba會捕獲sz,函式體將string的大小和捕獲到的sz進行比較。

備註:如果捕獲列表中沒有sz,則該lambda會出錯。
  • 呼叫find_if,查詢第一個長度大於等於sz的元素
auto wc = find_if(words.begin(), words.end(),
				[sz] (const string &a)
					{ return a.size() >= sz; });
/*
	這裡對find_if的呼叫返回一個迭代器,指向第一個長度不小於給定引數sz的元素。
	如果這樣的元素不存在,則返回words.end()的一個拷貝。
*/

4、for_each演算法

捕獲列表只用於區域性非static變數,lambda可以直接使用區域性static變數和它所在函式之外宣告的名字。
//列印長度大於等於給定值的單詞,每個單詞後面接一個空格
for_each(wc, words.end(),
	[] (const string &s) { cout << s << " ";});

5、完整的biggies(以及全部完整的原始碼)

/*
<span style="white-space:pre">	</span>函式功能:將單詞按從小到大的順序排列,並刪除重複的單詞;
<span style="white-space:pre">			</span>  再按單詞的長度由短到長排列。
<span style="white-space:pre">	</span>演算法函式:stable_sort();sort();
*/
#include <iostream>
#include <vector>
#include <string>
#include <algorithm>

using namespace std;

void elimDups(vector<string> &words);
bool isShorter(const string &s1, const string &s2);

void biggies(vector<string> &words,
<span style="white-space:pre">	</span>vector<string>::size_type sz);//尋找第一個大於等於給定長度的元素,一旦找到就可以計算出有多少元素的長度大於等於給定值。
string make_plural(size_t ctr, const string &word, const string &ending);


int main()
{
<span style="white-space:pre">	</span>
<span style="white-space:pre">	</span>vector<string> words;
<span style="white-space:pre">	</span>string val;
<span style="white-space:pre">	</span>cout << "Input the words: ";


<span style="white-space:pre">	</span>while(cin >> val)//用迴圈,輸入字串
<span style="white-space:pre">	</span>{
<span style="white-space:pre">		</span>words.push_back(val);
<span style="white-space:pre">	</span>}
<span style="white-space:pre">	</span>elimDups(words);//呼叫函式,排序,刪除


<span style="white-space:pre">	</span>stable_sort(words.begin(), words.end(), isShorter);
<span style="white-space:pre">	</span>cout << "The output is: ";//輸出排序刪除之後的單詞
<span style="white-space:pre">	</span>for(auto beg = words.begin(), end = words.end(); beg != end; ++beg)
<span style="white-space:pre">		</span>cout << *beg << " ";
<span style="white-space:pre">	</span>cout << endl;


<span style="white-space:pre">	</span>biggies(words,4);
<span style="white-space:pre">	</span>
<span style="white-space:pre">	</span>while(1);
<span style="white-space:pre">	</span>return 0;
}
void elimDups(vector<string> &words)
{
<span style="white-space:pre">	</span>sort(words.begin(), words.end());//按字典序排序
<span style="white-space:pre">	</span>//uinque重排輸入範圍,使得每個單詞只出現一次
<span style="white-space:pre">	</span>//排列在範圍的前部,返回指向不重複區域之後一個位置的迭代器
<span style="white-space:pre">	</span>auto end_unique = unique(words.begin(), words.end());
<span style="white-space:pre">	</span>//使用向量操作erase刪除重複單詞
<span style="white-space:pre">	</span>words.erase(end_unique, words.end());
}


bool isShorter(const string &s1, const string &s2)
{
<span style="white-space:pre">	</span>return s1.size() < s2.size();
}

void biggies(vector<string> &words,
<span style="white-space:pre">	</span>vector<string>::size_type sz)
{
<span style="white-space:pre">	</span>elimDups(words);//將words排序,刪除重複的單詞
<span style="white-space:pre">	</span>//按長度排序,長度相同的單詞維持字典序
<span style="white-space:pre">	</span>stable_sort(words.begin(), words.end(),
<span style="white-space:pre">				</span>[] (const string &a, const string &b)
<span style="white-space:pre">					</span>{ return a.size() < b.size();});
<span style="white-space:pre">	</span>//獲取第一個迭代器,指向第一個滿足size()>=sz的元素
<span style="white-space:pre">	</span>auto wc = find_if(words.begin(), words.end(),
<span style="white-space:pre">				</span>[sz] (const string &a)
<span style="white-space:pre">					</span>{ return a.size() >= sz; });
<span style="white-space:pre">	</span>//計算滿足size>=sz的元素數數目
<span style="white-space:pre">	</span>auto count = words.end() - wc;//此時的words是按長度由小到大排列的。
<span style="white-space:pre">	</span>cout << count << " " << make_plural(count , "word", "s")
<span style="white-space:pre">		</span> << " of length " << sz << " or longer" << endl;
<span style="white-space:pre">	</span>//列印長度大於等於給定值的單詞,每個單詞後面接一個空格
<span style="white-space:pre">	</span>for_each(wc, words.end(),
<span style="white-space:pre">		</span>[] (const string &s) { cout << s << " ";});
<span style="white-space:pre">	</span>cout << endl;
}

string make_plural(size_t ctr, const string &word,
<span style="white-space:pre">							</span>   const string &ending)
{
<span style="white-space:pre">	</span>return (ctr>1) ? word+ending : word;
}


  • 練習:編寫一個lambda,接受兩個int,返回它們的和
#include <iostream>

int main()
{
	using namespace std;

	int a,b;
	cout << "a = ";
	cin >> a;
	cout << "b = ";
	cin >> b;
	//auto f = [] (int a, int b) {return a+b;};//省略的返回型別int
	//cout << f(a,b) << endl;
	auto f = [] (int a, int b) ->int { return a+b;};
	cout << "a + b = " << f(a,b) << endl;

	cin.get();
	cin.get();
	return 0;
}


  • 例子:編寫一個lambda,捕獲它所在函式的int,並接受一個int引數,lambda應該返回捕獲的int和int引數的和。
#include <iostream>

int main()
{
	using namespace std;

	int a,val;
	cout << "a = ";
	cin >> a;
	cout << "val = ";
	cin >> val;

	auto f = [val] (int a) ->int { return a+val;};//接受一個引數,返回引數和捕獲的值的和
	cout << "a + val = " << f(a) << endl;

	cin.get();
	cin.get();
	return 0;
}


6、lambda捕獲和返回

當定義一個lambda時,編譯器生成一個與lambda對應的新的(未命名的)類型別。(暫時先不考慮這種類是如何生成的。) 可以這樣理解,當向一個函式傳遞一個lambda時,同時定義了一個新型別和該型別的一個物件:傳遞的引數就是此編譯器生成的類型別的未命名的物件。 類似地,當使用auto定義一個用lambda初始化的變數時,定義了一個從lambda生成的型別的物件。 預設情況下,從lambda生成的類都包含一個對應該lambda所捕獲的變數的資料成員。類似任何普通類的資料成員,lambda的資料成員也在lambda物件建立時被初始化
  • 值捕獲
與引數不同的是:被捕獲的變數的值是在lambda建立時拷貝的,而不是呼叫時拷貝。 引數是在呼叫的時候才拷貝。採用值捕獲的前提是變數可以拷貝。 [ val ] { return val; }
  • 引用捕獲
採用引用的方式捕獲變數。當以引用方式捕獲一個變數時,必須保證在lambda執行時變數是存在的。 [ &val ] { return val; }
  • 隱式捕獲
為了指示編譯器推斷捕獲列表,應在捕獲列表中寫一個 & 或 = 。&告訴編譯器採用捕獲引用方式, = 則表示採用值捕獲方式。
//sz為隱式捕獲,值捕獲方式
wc = find_if(words.begin(), words.end(),
			[=] (const string &s)
				{ return s.size() >= sz;});

void biggies(vector<string> &words, vector<string>::size_type sz,
			ostrem & os = cout ,char c = ' ')
{
	//os隱式捕獲,引用捕獲方式;c顯示捕獲,值捕獲方式
	for_each(words.begin(), words.end(), 
		[&, c] (const string & s){ os << s << c; });
	//os顯示捕獲,引用捕獲方式;c為隱式捕獲,值捕獲方式
	for_each(words.begin(), words.end(), 
		[=, &os] (const string & s) { os << s << c; });
}
  • 可變lambda
如果我們希望能改變一個被捕獲的變數的值,就必須在引數列表首加上關鍵字mutable。
void fcn3()
{
	size_t v1 = 42;
	//f可以改變它所捕獲的變數的值
	auto f = [v1] () mutable { return ++v1;};
	v1 = 0;
	auto j = f();//j = 43
}

void fcn4()
{
	size_t v1 = 42;
	//可以通過f2的引用來改變它
	auto f2 = [&v1] { return ++v1;};
	v1 = 0;
	auto j = f2();//j = 1;	
}
//一個引用捕獲的變數是否可以修改依賴於此引用指向的是一個const型別還是一個非const型別。

  • 指定lambda返回型別
使用標準庫transform演算法和一個lambda來將一個序列中的每個負數替換為其絕對值:
transform(vi.begin(), vi.end(), vi.begin(),
			[] (int i) { return i < 0 ? -i : i; });
/*
	transform接受三個迭代器和一個可呼叫物件。
	前兩個迭代器表示輸入序列,第三個迭代器表示目的位置。
	演算法對輸入序列中的每個元素呼叫可呼叫物件(lamdba),
	並將結果寫到目的位置;
	此時編譯器可以推斷出,lamdba的返回型別是 bool型別
*/

當我們需要為一個lambda定義返回型別時,必須使用尾置返回型別
transform(vi.begin(), vi.end(), vi.begin(),
	[] (int i) -> int { if(i < 0) return -i; else return i;});
/*
	如果沒有尾置返回型別int,則編譯器會推斷這個lamdba返回型別為void,
	但是返回了一個int值,所以會出錯。
*/

總結:

  • 對於那種只在一兩個地方使用的簡單操作,lambda表示式是最有用的。如果我們需要在很多地方使用相同的操作,應該定義一個函式。
  • 如果一個操作需要很多語句才能完成,通常使用函式。
  • 如果lambda的捕獲列表為空,通常可以用函式來代替它。
  • 對於捕獲區域性變數的lambda,用函式來替代就不行了。比如:find_if演算法,接受一個一元謂詞,因此可以是一個lambda,而不能是一個函式。

相關推薦

學習C++——lambda表示式

上面三個是C++學習網站,有助於瞭解C++11的新特性。 lambda表示式(C++11) 1、介紹lambda 我們使用過的僅有兩種可呼叫物件是函式和函式指標。還有其他兩種可呼叫物件:過載了函式呼叫運算子的類,以及lambda表示式。 一個lambda表示式表示一個

[effective modern c++ 學習筆記] Lambda 表示式 徹底拋棄bind

要點總結 <1> lambda基本形式 [捕獲列表](形參表){函式體} std::find_if(container.begin(), container.end(),[](int val) { return 0 < val && val <

java8學習lambda表示式(1)

內容來自《 java8實戰 》,本篇文章內容均為非盈利,旨為方便自己查詢、總結備份、開源分享。如有侵權請告知,馬上刪除。書籍購買地址:[java8實戰] 上一篇內容解釋了行為引數化的概念並用例項演示瞭如何是行為引數化,文末提到了lambda表示式,那麼本文將講解lambda表示式

C++ lambda表示式總結

    一個lambda表示式用於建立閉包。lambda表示式與任何函式類似,具有返回型別、引數列表和函式體。與函式不同的是,lambda能定義在函式內部。lambda表示式具有如下形式 [ capture list ] ( parameter list) -> r

使用ES6新陣列方法(象C# Lambda表示式一樣寫查詢語句)

let people = [ {id: 1, name: "a", age: 12}, {id: 2, name: "b", age: 13}, {id: 3, name: "c", age: 14}, {id: 4, name: "d", age: 15}

C++ lambda表示式

1. lambda表示概念 可將lambda表示式視為包含公有operator()的匿名結構(或類),從這種意義上說,lambda表示式屬於函式物件。從上面所講到的進行分析: for_each(vectorElement.begin(), vectorElement.en

c++ lambda表示式常用的情形

lambda表示式介紹 懶,摘自msdn Capture 子句(在 C++ 規範中也稱為 lambda 引導。) 引數列表(可選)。 (也稱為 lambda 宣告符) 可變規範(可選)。 異常規

C++ lambda表示式入門

1.lambda表示式 lambda表示式 是一個函式,一個匿名函式,也就是沒有函式名的函式,為什麼不需要函式名呢,因為我們直接(一次性的)用它,嵌入式用的它,不需要其他地方用它。 也叫閉包,閉就是封閉的意思,就是其他地方都不用他,包就是函式。 lambda表示

C++ lambda表示式一個非常簡單的例子

如果我想要定義一個指向int為返回值,兩個int為引數的函式的指標,有兩種方法1: 先宣告一個函式int func(int, int);然後使用decltype推斷這個func的型別作為新定義的型別的型別typedef decltype(func) * Func1;2: 直接

python學習lambda表示式或引數作為表示式

import numpy as np lambda表示式或引數作為表示式 1、函式名字作為引數或者lambda表示式作為引數 def lambda_funt(a,b,fun): return fun(a,b) 2、加法 def a

C#Lambda表示式

需求 有時候我們需要傳遞一個很方法的引用,我們很確定這個方法僅僅會呼叫這一次,單獨為它建立一個方法感覺有些浪費,但是又必須用到這個方法。又或者臨時需要一個方法,但是思考半天想不出該給這個方法取什麼名字(有過這個經歷的同學握個爪)。這個時候Lambda就派上用場

C# Lambda表示式詳細總結

(一)輸入引數 在Lambda表示式中,輸入引數是Lambda運算子的左邊部分。它包含引數的數量可以為0、1或者多個。只有當輸入引數為1時,Lambda表示式左邊的一對小括弧才可以省略。輸入引數的數量大於或者等於2時,Lambda表示式左邊的一對小括弧中的多個引

C# => Lambda表示式理解

本文參考網上的部落格和找到的資料加上個人理解編寫的,主要的程式碼借鑑:http://www.cnblogs.com/knowledgesea/p/3163725.html、百度百科 希望能夠幫助理解l

C# Lambda表示式詳細

(一)輸入引數 在Lambda表示式中,輸入引數是Lambda運算子的左邊部分。它包含引數的數量可以為0、1或者多個。只有當輸入引數為1時,Lambda表示式左邊的一對小括弧才可以省略。輸入引數的數量大於或者等於2時,Lambda表示式左邊的一對小括弧中的多個引

C++ lambda 表示式傳遞的變數預設不可變

我遇到如下問題: int count=0; listener->onTouchMoved=[count](Touch* t,Event* e){ count++; log("onTouchMoved");

C++ lambda表示式的編譯器實現..

 現在,Android已經全面轉向C++11/14標準了,看程式碼的話,很多地方變化很大,新標準真的是有點顛覆性的,感覺已經不會C++了。今天有看到lambda表示式,突然想看一下,這貨是怎麼實現的,如下,寫了個例子,分別呼叫3個lambda表示式: #include

C# Lambda表示式

上一節中,我們講到:在 2.0 之前的 C# 版本中,宣告委託的唯一方法是使用命名方法。  C# 2.0 引入了匿名方法,而在 C# 3.0 及更高版本中,Lambda 表示式取代了匿名方法,作為編寫內聯程式碼的首選方式。  有一種情況下,匿名方法提供了 Lambda 表示式

c++ lambda表示式(匿名函式)的使用總結

#include <iostream> using namespace std; //[=] () mutable throw() -> int {} /** * lambda expression * “Lambda 表示式”(lambda exp

C#lambda表示式和匿名函式

lambda表示式也被稱為匿名函式,何為匿名函式? 匿名沒有真實名字,當然在C#中就是沒有函式名了,C#裡有兩種匿名函式的寫法,一種是早期推出的匿名函式,而另一種就是拉姆達(lambda)表示式了,那麼這兩種有什麼不同,分別怎麼用的,下面介紹一下. 我個人覺得吧,在寫程式碼

c++ lambda表示式捕獲變數引數

[]不捕獲任何變數 [&]捕獲外部作用域中所有變數,並作為引用在函式體重使用 [=]捕獲外部作用域中所有變數,並作為副本在函式體重使用 [=,&foo]捕獲外部作用域中所有變數,並作為副本在函式體重使用,對於foo按引用捕獲 [foo]當作副本捕獲foo,不