(轉載)C++lambda表達式

(轉載) http://www.cnblogs.com/L-hq815/archive/2012/08/23/2653002.html

lambda表達式

C++ 語言中的lambda表達式在不少狀況下提供了函數對象的另外一種實現機制。Lambda表達式並非STL所特有的,但它普遍應用於這一環境中。Lambda是表達式是定義一個沒有名稱、也不須要顯示類定義的函數對象。Lambda表達式通常做爲一種手段,用來將函數做爲實參傳遞到另外一個函數。相比於定義和建立一個常規的函數對象而言,lambda表達式很是容易使用和理解,並且須要的代碼也較少。固然,通常而言,lambda表達式並不會取代函數對象。html

舉個例子,假設有個包含數值的矢量,咱們計算此矢量的立方值。能夠用transform()函數操做,簡單的用lambda表達式完成。ios

double  values[] = {1,2,3,4,5,6};
vector<double> data(values,values+6);
vector<double> cubes(data.size());
transform(values.begin(),values.end(),cubes.begin(),[](double x){ return x*x*x;});

最後這條語句用來計算data中的立方值,並存儲在cubes。這裏簡單提一下transform()函數。它是algorithm頭文件中的函數,它有兩個版本。express

第一個版本是將一個一元函數對象指定的操做應用到由一對迭代器指定的一個元素集合上,格式以下:dom

transform(InputIterator begin, InputIterator end, OutputIterator result,UnaryFuncton f);

transform()的這個版本將一元函數f應用到迭代器begin 和end指定的範圍中的全部元素,並從迭代器result指定的位置開始存儲結果。Result迭代器能夠與begin迭代器相同,只是在這種狀況下將會替換原有的內容。這個函數返回一個迭代器,指向存儲的最後一個結果的下一個位置。函數

舉例以下:spa

double  values[] = {1,2,3,4,5,6};
vector<double> data(values,values+6);
transform(values.begin(),values.end(),values.begin(),negate<double>);

transform()函數調用negate<double>函數對象應用到矢量data中的全部元素,結果存儲在data中,並重寫了原始值,執行完後data將包含 -1,-2,-3,-4,-5,-6。函數返回迭代器data.end()。code

transform()第二個版本經過來自迭代器指定的兩個範圍內的操做數應用一個二元函數。格式爲:orm

transform(InputIterator1 begin1, InputIterator 1end1, InputIterator2 begin2, OutputIterator result,BinaryFunction f);

由begin1和end1指定的範圍表示最後一個實參指定的二元函數f的左操做數集合。表示右操做的範圍從begin2迭代器指定的位置開始,這個範圍不須要提供end迭代器,由於這個範圍的元素數量必須與begin1和end1指定的範圍元素個數相同,結果從result迭代器位置開始存儲在這個範圍內。若是但願存回原範圍中,result迭代器能夠與begin1相同。htm

舉例以下:對象

複製代碼
double values[]={2.5,-3.5,4.5,-5.5,6.5,-7.5};
vector<double> data(values, values + sizeof values / sizeof values[0]);
vector<double> squares(data.size());
transform(data.begin(),data.end(),data.begin(),squares.begin(),multiplies<double>());
ostream_iterator<double> out(cout,」 「);
copy(squares.begin(),squares.end(),out);
複製代碼

Transform()函數經過multiplies<double>函數對象將自身相乘,結果存儲到squares中。最後兩句用一個輸出迭代器輸出內容。

如今回到上文:

transform(values.begin(),values.end(),cubes.begin(),[](double x){ return x*x*x;});

開始的方括號稱爲lambda引導,它標誌着lambda表達式的開始。後面的圓括號中的是lambda的參數列表,這與普通函數相同。此例中只有一個形參x。注意,lambda的參數列表不容許指定形參的默認值,而且參數列表的長度是不可變的。大括號中的是lambda的主體,此例只有一條return語句,固然能夠包含多條語句。你們可能注意到這裏沒有返回類型說明。當lambda表達式的主體是一條單一返回語句,而該語句在lambda表達式主體中返回一個值時,返回類型默認爲返回值的類型。不然,返回void。固然能夠指定返回類型,以下:

[](double x) ->double{ return x*x*x;} //指定返回double

Capture子句

lambda表達式引導能夠包含一個捕獲子句,用來肯定lambda主體如何訪問封閉做用域中的變量。前面lambda表達式方括號之間沒有內容,表面封閉做用域沒有能夠再lambda表達式中訪問的變量。若要訪問,第一種是方括號之間是 = ,則lambda主體能夠按值訪問封閉做用域的全部自動變量,但不會修改原始變量。另外一中是方括號之間是 & ,則封閉做用域的全部自動變量按應用訪問,所以lambda表達式能夠修改變量值。例如:

複製代碼
double index = 3.0;
double  values[] = {1,2,3,4,5,6};
vector<double> data(values,values+6);
vector<double> cubes(data.size());
transform(values.begin(),values.end(),cubes.begin(),
[=](double x){ return index*x*x*x;});
複製代碼

須要主要的是,這與按值傳遞實參根本不一樣,變量index的值可用在lambda中,但不能更新index的副本。如:

transform(values.begin(),values.end(),cubes.begin(),
[=](double x) ->double{ 
index += 10; // error 
return index*x*x*x;});

以上是錯誤的,若要修改變量的臨時副本,則經過添加mutable關鍵字實現。如:

transform(values.begin(),values.end(),cubes.begin(),
[=](double x)mutable ->double{ 
index += 10; // ok
return index*x*x*x;});

如今能夠修改做用域中的任意變量副本,而不會修改原始值。

transform(values.begin(),values.end(),cubes.begin(),
[&](double x)mutable ->double{ 
index += 10; // change original value
return index*x*x*x;});

如今採用按引用使用,則會改變index的原始值。

若要捕獲一個特定的變量,則:

transform(values.begin(),values.end(),cubes.begin(),
[&index](double x)mutable ->double{ 
index += 10; // change original value
return index*x*x*x;});

這樣,只捕獲index,如要捕獲多個變量,中間用逗號隔開便可。

Lambda也能夠包含throw()異常說明,如:

transform(values.begin(),values.end(),cubes.begin(),
[&index](double x)mutable throw()->double{ 
index += 10; // change original value
return index*x*x*x;});

若是想要包含mutable說明和throw()說明,則中間必須用一個或多個空格隔開。

如今綜合看個實例,用之前說過的函數模板實現。

複製代碼
// Using lambda expressions
#include <algorithm>
#include <iostream>
#include <iomanip>
#include <vector>
#include <ctime>
#include <cstdlib>
using namespace std; // Just to avoid a lot of using directives in the example...

// Template function to return the average of the elements in a vector
template <class T> T average(const vector<T>& vec)
{
    T sum(0);
    for_each(vec.begin(), vec.end(),
        [&sum](const T& value){ sum += value; });
    return sum/vec.size();
}

// Template function to set a vector to values beginning with start and incremented by increment
template <class T> void setValues(vector<T>& vec, T start, T increment)
{
    T current(start);
    generate(vec.begin(), vec.end(), 
        [increment, &current]()->T{T result(current);
    current += increment;
    return result;});
}

// Template function to set a vector to random values between min and max
template<class T> void randomValues(vector<T>& vec, T min, T max)
{
    srand(static_cast<unsigned int>(time(0)));   // Initialize random number generator
    generate(vec.begin(), vec.end(),
        [=](){ return static_cast<T>(static_cast<double>(rand())/RAND_MAX*(max-min)+min); });
}

// Template function to list the values in a vector
template<class T> void listVector(const vector<T>& vec)
{
    int count = 0;      // Used to control outputs per line
    for_each(vec.begin(), vec.end(),
        [&count](const T& n)->void{  cout << setw(10) << n;
    if(++count % 5)
        cout << "  ";
    else
        cout << endl;});
}

int main()
{
    vector<int> integerData(50);
    randomValues(integerData, 10, 100);    // Set random integer values
    cout << "Vector contains:" << endl;
    listVector(integerData);
    cout << "Average value is "<< average(integerData) << endl;

    vector<double> realData(20);
    setValues(realData, 5.0, 2.5);   // Set real values starting at 5.0 ,increment by 2.5
    cout << "Vector contains:" << endl;
    listVector(realData);
    cout << "Average value is "<< average(realData) << endl;

    return 0;
}

複製代碼

 

Lambda表達式的包裝

Lambda表達式的包裝其實是使用function< >模板賦予lambda表達式一個名字,這不只提供了在Lambda表達式內遞歸的可能,並且能夠再多條語句使用一樣的lambda表達式。如:

Function< int (double)> f = [](double x){ return static_cast<int>(x*x)};

這裏具備一個double類型的形參,並返回一個爲int類型的值,固然,此處只是舉例而已,由於該語句存在問題,將double賦給int時可能會丟失數據。

舉例:

複製代碼
#include <iostream>
#include <functional>
using std::function;
using std::cout;
using std::endl;
                        
int main()
{
  // Wrap the lambda expression to compute the HCF
  function<int(int,int)> hcf = [&](int m, int n) mutable ->int{ if(m < n) return hcf(n,m);
                                     int remainder(m%n);
                                     if(0 == remainder) return n;
                                     return hcf(n, remainder);};
  int a(17719), b(18879);
  cout << "For numbers " << a << " and " << b << " the HCF is " << hcf(a, b) << endl;
  a = 103*53*17*97;
  b = 3*29*103;
  cout << "For numbers " << a << " and " << b << " the HCF is " << hcf(a, b) << endl;

   return 0;
}
複製代碼

該實例用歐幾里得法求兩個數的最大公約數,即所謂展轉相除法,採用遞歸形式實現。

相關文章
相關標籤/搜索