程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> C++ >> C++入門知識 >> 淺談C++ Lambda 表達式(簡稱LB),lambdalb

淺談C++ Lambda 表達式(簡稱LB),lambdalb

編輯:C++入門知識

淺談C++ Lambda 表達式(簡稱LB),lambdalb


C++ 11 對LB的支持,對於喜歡Functional Programming的人來說,無疑是超好消息。它使得C++進入了和C#,JavaScript等現代流行的程序設計語言所代表的名人堂。

不熟悉LB本身的網友,可以看MSDN文章

( http://msdn.microsoft.com/en-us/library/dd293608.aspx ),我僅僅簡單地分析一下VC++中LB的用法,實現,和性能。

無名引用

對於一次性的,帶參數表達式,用LB可以節省不必要的class定義和維護,簡化程序的設計-維護代價。

比如下面的vector處理代碼,簡潔明了:

vector<int> v1(10, 1);

int sum = 0;

for_each (v1.begin(), v1.end(), [&](int i){ sum += i; })//Line1

 

否則,我們必須定義一個function類,把如此簡單的事情復雜化。用了LB,我們把定義function 類的工作,轉交給編譯。VC++中,上述LB編譯的實現是產生一個隱身類:

 

class  _lambda_a01 {

    int &capture1_;

public:

  _lambda_a01(int &x): capture1_(x) {}  //Line2

 operator void (int i) { capture1_ += I; }

};

 

在引用時(Line1),它變成:

 

_lambda_a01 lbd1sum);

forauto av1{

   ldb1a);

}

 

讀者也許好奇,為什麼C++不直接把LB轉換成inline expression (inline 表達式),而是要生成一個隱身類呢?這是因為LB的確可以當成“type”變量來用,這樣使得LB和其他類有了同等地位。比如:

 

vector<int> v1(10, 1);

int sum = 0;

for_each (v1.begin(), v1.end(), [&](int i){ sum += i; })//Line1

vector<int> v2(10, 1);

int sum2 = 0;

for_each (v1.begin(), v1.end(), [&](int i){ sum2 += i; })//Line2

 

我們如果用上述的方法,Line1Line2重復代碼,是軟件工程的大忌。我們可以用下列LB使用模式:

有名無型引用

 

vector<int> v1(10, 1);

vector<int> v2(10, 1);

int sum = 0;

auto lb = [&](int i){ sum += i; }  //Line0

 

for_each (v1.begin(), v1.end(), lb)//Line1

sum = 0;                              // Line1.1

for_each (v1.begin(), v1.end(), lb})//Line2

 

Line0,我們定義了一個有名(lb)無型的LB,可以在Line1Line2重復使用。

 

注意的是,

1) 每個LB的“定義”都會產生新的“隱身”類,所以盡量用“有名引用”,會減少代碼的size,縮小工作集。

2) 定義時,LB一次性“俘獲”環境變量,所以上面修改後的代碼加了Line1.1,以便正確表達應用邏輯。

3) 俘獲可以是“傳值(by value)”也可以是“傳引用(by reference)。我們Line0用的是by reference.

 

有名有型引用

上面兩種LB使用模式,是LB應用的主要模式,它直接反映出了LB的優點。另一方面說,既然LB無非是隱身類,我們沒有理由不能把它當作普通變量使用。這個模式是一種簡化的functor使用模式。我們可以把LB定義成一個std::function,比如上面的auto lb可以定義成:

std::function <void(int)> lb; //lb is a function which takes an integer and returns void

 

注意到用這個定義,使得我們可以推遲給LB變量賦值,甚至一變量賦多址(不同時間)。下面就是一個簡單用例:

struct MyLambda

{

       std::function <int (int)> _lbda;//line1

       int _extra;

};

 

MyLambda TestLambdaObj(int t)

{

       MyLambda ret;

       if (t == 1)

       {

             ret._extra = t;

             ret._lbda = [=](int x)  -> int { return t + x; }; //line2

             return ret;

       }

       else

       {

             ret._extra = t;

             ret._lbda = [=](int x)  -> int { return t * x; };//line3

             return ret;

       }

}

 

void TestLambdaFun2(int t)

{

       MyLambda ret = TestLambdaObj(t);

       int v = ret._lbda(t);                                //line4

       printf("v is '%d' for type %d", v, t);

}

 

我們先定義MyLambda數據類,並與其定義了一了function成員_lbda,根據C++ SPEC,他可以由LB轉換構造,並且和普通的類變量無甚區別。然後我們可以運行時給它賦值(line2line3), 當作普通function來使用(line4)。

注意的是:

  • function的定義中沒有“閉包”的概念,閉包的形成是在LB創建時實現(line2line3)。
  • 把LB賦值給function變量,必然造成調用時(line4)的間接性(通過函數指針),其性能相當於虛擬函數,也不能inline化,當然比直接調用有所下降。

閉包(closure)是LB的獨特附加值

如果你問為什用LB而不用std::function?我的回答是“閉包”。

C++用LB來實現閉包,是一個簡化繁瑣的class初始化的syntax sugar。這一點是std::function所不可替代的。比如說:

auto sum = 0;

auto step = 2;

auto lb = [&](int i){ sum += i + step; }//capture sum and step by ref

 

lb形成自己的閉包,自動從環境中俘獲了sumstep,若用class實現,上面的程序起碼增加10行代碼。

 

LB性能初探

下面的簡單程序,測試四種功能完全一樣,但使用不同表達式的邏輯:

1)t =1 時用LB,

2)t=2 時用直接表達式

3)t=3 時用函數

4)t=4時用std::function間接調用LB

 

void TestLambdaFun(int t)

{

       using namespace std;

       vector<int> v1(10, 1);

       int x = 0;

       int u = 0;

       if (t == 1)

       {

             clock_t begin = clock();

             for (int i = 0; i < 100000; ++i)

             {

                    for_each (v1.begin(),

                              v1.end(),

                          [&x, &u](int i){ u += i+(x++); });// Line 1

             }

             clock_t end = clock();

             auto spent = double(end - begin) / CLOCKS_PER_SEC;

             printf("spent for type '%d' is %f u is %d\n", t, spent, u);

       }

       else if (t == 2)

       {

             clock_t begin = clock();

             for (int i = 0; i < 100000; ++i)

             {

                    auto _First = v1.begin();

                    auto _Last = v1.end();

                    for (; _First != _Last; ++_First)

                    {

                           u = *_First+(x++);                  // Line 2

                    }

             }

             clock_t end = clock();

             auto spent = double(end - begin) / CLOCKS_PER_SEC;

             printf("spent for type '%d' is %f u is %d\n", t, spent, u);

       }

       else if (t == 3)

       {

             clock_t begin = clock();

             for (int i = 0; i < 100000; ++i)

             {

                    auto _First = v1.begin();

                    auto _Last = v1.end();

                    for (; _First != _Last; ++_First)

                    {

                           FuncAdd(u, x, *_First);             // Line 3

                    }

             }

             clock_t end = clock();

             auto spent = double(end - begin) / CLOCKS_PER_SEC;

             printf("spent for type '%d' is %f u is %d\n", t, spent, u);

       }

      else if (t == 4)

       {

              clock_t begin = clock();

              std::function <void (int)> lbda;

              for (int i = 0; i < 100000; ++i)

              {

                    lbda = [&](int i){ u += i + (x++); };

                    for_each (v1.begin(), v1.end(), lbda); // Line 4

              }

              clock_t end = clock();

              auto spent = double(end - begin) / CLOCKS_PER_SEC;

              printf("spent for type '%d' is %f u is %d\n", t, spent, u);

       }

 

}

 

void FuncAdd(int &u, int &x, int i)

{

       u = i+(x++);

}

 

下面是VC++ 2010中的測試結果:

 

  • debug模式下,t=2時速度最快,這是因為t=1,t=3,t=4時都是用了函數調用,性能當然不及inline表達式。
  • release模式下(選擇/Ob1優化,對inline函數進行inline擴展)
    • t=1和t=2速度完全一樣,比t=3時平均快3倍。當然,我們也可以把FuncAdd inline化。這裡的主要目的,是證明優化後,LB的性能和表達式完全一樣。證明C++ lambda expression不是浪得虛名的隱身類的syntax sugar,而是名副其實的“表達式”。

t=4最慢,它和t=3類似。但是由於通過了std::function的虛擬函數表間接調用,/Ob1優化失去作用,使它不但要調用一個() operator,而且是通過“虛擬表”間接調用。所以從性能上說,把LB通過std::function間接使用,失去了LB的性能優勢。

總結

C++ 11 的lambda expression(簡稱LB),在可以保證和inline expression同樣性能的條件下,增加了參數功能和閉包功能,是我們寫出簡潔,明了,易維護代碼的絕佳工具。應用時,為了避免代碼重復和增加隱身類的數量,可用有名無型的LB變量。LB也可以賦值於std::function,當作函數指針使用,但是性能不及簡單地inline使用。


Lambda表達式

相當於是這樣的
class test
{
test(Racer r, Car c)
{
this.Racer = r;
this Car = c;
}
Racer{get;set;}
Car {get;set;}
}

test t = new test(r,c);
 

C#中 Lambda表達式 與正則表達式的關系

八竿子打不著的關系
lambda表達式是函數式語言的特性,c#利用自身動態編譯的特性才實現這種效果的,可以用它寫匿名函數等.像C,C++神馬的都沒有的.
正則是各語言都有實現的,用來查詢匹配等的
 

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved