zoukankan      html  css  js  c++  java
  • lambda表达式

    根据算法接受一元谓词还是二元谓词,我们传递给算法的谓词必须严格接受一个或两个参数。但是,有时我们希望进行的操作需要更多参数,超出了算法对谓词的限制。

    如果在编写划分序列的谓词时,可以不必为每个可能的大小都编写一个独立的谓词,显然更有实际价值。

    一个相关的例子是,求大于等于一个给定长度的单词有多少。我们还会修改输出,使程序只打印大于等于给定长度的单词。我们将此函数命名为biggies,其框架如下:

    void biggest(vector<string> &words, vector<string>::size_type sz){
        elimDups(words);   // 将words按照字典序排列,删除重复单词
        // 按长度排序,长度相同的维持字典序
        stable_sort(words.begin(), words.end(), isShorter);
        // 获取一个迭代器,指向第一个满足size() >= sz的元素
        // 计算满足size >= sz的元素的数目
        // 打印长度大于等于给定值的单词
    }
    

    我们的新问题是在 vector 中寻找第一个大于等于给定长度的元素。一旦找到了这个元素,根据其位置,就可以计算出有多少元素的长度大于等于给定值。

    我们可以使用标准库 find_if 算法来查找第一个具有特定大小的元素。类似 find,find_if 算法接受一对迭代器,表示一个范围。但与find不同的是,find_if 的第三个参数是一个谓词。find_if 算法对输入序列中的每个元素调用给定的这个谓词。它返回第一个使谓词返回非0值的元素,如果不存在这样的元素,则返回尾迭代器。

    编写一个函数,令其接受一个 string 和一个长度,并返回一个 bool 值表示该 string的长度是否大于给定长度,是一件很容易的事情。

    但是,find_if接受一元谓词——我们传递给 find_if 的任何函数都必须严格接受一个参数,以便能用来自输入序列的一个元素调用它。没有任何办法能传递给它第二个参数来表示长度。为了解决此问题,需要使用另外一些语言特性。

    介绍lambda

    我们可以向一个算法传递任何类别的可调用对象(callable object)。对于一个对象或一个表达式,如果可以对其使用调用运算符,则称它为可调用的。

    即,如果e是一个可调用的表达式,则我们可以编写代码 e(args),其中args是一个逗号分隔的一个或多个参数的列表。

    一个 lambda 表达式表示一个可调用的代码单元。我们可以将其理解为一个未命名的内联函数。与任何函数类似,一个 lambda 具有一个返回类型、一个参数列表和一个函数体。

    但与函数不同,lambda可能定义在函数内部。一个lambda 表达式具有如下形式

    [capture list](parameter list) -> return tyne { function body }

    其中,capture list(捕获列表)是一个lambda所在函数中定义的局部变量的列表(通常为空);

    renurn type、parameter list 和function body与任何普通函数一样,分别表示返回类型、参数列表和函数体。但是,与普通函数不同,lambda 必须使用尾置返回来指定返回类型。

    我们可以忽略参数列表和返回值类型, 但必须永远包含捕获列表和函数体

    auto f = [] { return 42; };
    

    此例中,我们定义了一个可调用对象f,它不接受参数,返回42。

    lambda的调用方式与普通的函数的调用方式相同,都是使用调用运算符:

    cout << f() << endl;
    

    在 lambda 中忽略括号和参数列表等价于指定一个空参数列表。

    在此例中,当调用 f时,参数列表是空的。如果忽略返回类型,lambda根据函数体中的代码推断出返回类型。如果函数体只是一个return 语句,则返回类型从返回的表达式的类型推断而来。否则,返回类型为void。

    向lambda传递参数

    与一个普通函数调用类似,调用一个 lambda 时给定的实参被用来初始化 lambda的形参。

    通常,实参和形参的类型必须匹配。但与普通函数不同,lambda不能有默认参数。因此,一个 lambda调用的实参数目永远与形参数目相等。一旦形参初始化完毕,就可以执行函数体了。

    作为一个带参数的 lambda 的例子,我们可以编写一个与isShorter 函数完成相同功能的lambda∶

    [](const string &a, const string &b) { return a.size() < b.size(); }
    

    空捕获列表表明此 lambda 不使用它所在函数中的任何局部变量

    lambda 的参数与 isShorter 的参数类似,是 const string 的引用。lambda 的函数体也与isShorter类似,比较其两个参数的 size(),并根据两者的相对大小返回一个布尔值。

    如下所示,可以使用此 lambda 来调用 stable_sort∶

    stable_sort(words.begin(), words.end(),
                [](const string &a, const string &b) { return a.size() < b.size(); });
    

    当stable_sort需要比较两个元素时,它就会调用给定的这个lambda表达式。

    使用捕获列表

    我们现在已经准备好解决原来的问题了——编写一个可以传递给 find_if 的可调用表达式。

    我们希望这个表达式能将输入序列中每个string 的长度与biggies 函数中的 sz参数的值进行比较。

    虽然一个 lambda 可以出现在一个函数中,使用其局部变量,但它只能使用那些明确指明的变量。一个 lambda通过将局部变量包含在其捕获列表中来指出将会使用这些变量。捕获列表指引 lambda 在其内部包含访问局部变量所需的信息。

    在本例中,我们的 lambda 会捕获 sz,并只有单一的 string 参数。其函数体会将 string 的大小与捕获的 sz 的值进行比较:

    [sz](const string &a){ return a.size() >= sz; };
    

    lambda 以一对[ ] 开始,我们可以在其中提供一个以逗号分隔的名字列表,这些名字都是它所在函数中定义的。

    由于此 lambda捕获 sz,因此 lambda 的函数体可以使用sz。lambda 不捕获words,因此不能访问此变量。如果我们给 lambda提供一个空捕获列表,则代码会编译错误∶

    调用find_if

    使用此lambda,我们就可以查找第一个长度大于sz的元素:

    // 获取迭代器,指向第一个满足size() >= sz的元素
    auto wc = find_if(words.begin(), words.end(), 
                      [sz](const string &a) { return a.size() >= sz;} );
    

    这里对 find_if 的调用返回一个迭代器,指向第一个长度不小于给定参数 sz 的元素。如果这样的元素不存在,则返回 words.end()的一个拷贝。

    我们可以使用find_if返回的迭代器来计算从它开始到words的末尾一共有多少个元素

    auto count = words.end() - wc;
    

    for_each算法

    问题的最后一部分是打印words 中长度大于等于sz的元素。

    为了达到这一目的,我们可以使用 for_each 算法。此算法接受一个可调用对象,并对输入序列中每个元素调用此对象:

    // 打印长度大于等于给定值的单词
    for_each (wc, words.end(), [](const string &s) { cout << s << " "; });
    cout << endl;
    

    此 lambda中的捕获列表为空,但其函数体中还是使用了两个名字:s和cout,前者是它自己的参数。

    捕获列表为空,是因为我们只对 lambda所在函数中定义的(非static)变量使用捕获列表。一个lambda可以直接使用定义在当前函数之外的名字。在本例中,cout 不是定义在biggies中的局部名字,而是定义在头文件iostream中。因此,只要在biggies出现的作用域中包含了头文件 iostream,我们的lambda就可以使用 cout。

    完整的biggies

    void biggest(vector<string> &words, vector<string>::size_type sz){
        elimDups(words);     // 将words按照字典排序,删除重复单词
        // 按长度排序,长度相同单词维持字典排序
        stable_sort(words.begin(), words.end(),
                    [](const string &a, const string &b) { return a.size() < b.size();} );
        // 获取一个迭代器,指向第一个满足size() >= sz的元素
        auto wc = find_if(words.begin(), words.end(),
                          [sz](const string &a) { return a.size() > sz;} );
        // 计算满足size >= sz的元素的数目
        auto count = words.end() - wc;
        cout << count << " " << make_plural(count, "word", "s")<< " of length " << sz << " or longer " << endl;
        // 打印
        for_each(wc, words.end(), [](const string &s) { cout << s << " ";} );
        cout << endl;
    }
    
  • 相关阅读:
    “XXXXX” is damaged and can’t be opened. You should move it to the Trash 解决方案
    深入浅出 eBPF 安全项目 Tracee
    Unity3d开发的知名大型游戏案例
    Unity 3D 拥有强大的编辑界面
    Unity 3D物理引擎详解
    Unity 3D图形用户界面及常用控件
    Unity 3D的视图与相应的基础操作方法
    Unity Technologies 公司开发的三维游戏制作引擎——Unity 3D
    重学计算机
    windows cmd用户操作,添加,设备管理员组,允许修改密码
  • 原文地址:https://www.cnblogs.com/lihello/p/14332527.html
Copyright © 2011-2022 走看看