zoukankan      html  css  js  c++  java
  • C++ Lambda 表达式使用详解

    转载自:  http://www.codeceo.com/article/cpp-lambda.html

    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 lbd1(sum);

    for(auto a:v1){

    ldb1(a);

    }

    读者也许好奇,为什么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 ”, 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 ”, 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 ”, 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 ”, 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使用。

  • 相关阅读:
    hdu 2019 数列有序!
    hdu 2023 求平均成绩
    HDU 5805 NanoApe Loves Sequence (思维题) BestCoder Round #86 1002
    51nod 1264 线段相交
    Gym 100801A Alex Origami Squares (求正方形边长)
    HDU 5512 Pagodas (gcd)
    HDU 5510 Bazinga (字符串匹配)
    UVALive 7269 Snake Carpet (构造)
    UVALive 7270 Osu! Master (阅读理解题)
    UVALive 7267 Mysterious Antiques in Sackler Museum (判断长方形)
  • 原文地址:https://www.cnblogs.com/coder-zhang/p/3970080.html
Copyright © 2011-2022 走看看