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

    一、谓词&向算法传递函数

    1. 某些算法其中一个参数可以是谓词

    2. 谓词:一个可调用的表达式,其返回结果是一个能用作条件的值

    bool cmp(const int &a, const int &b)
    {
    	return a < b;
    } 
    
    	sort(vec.begin(), vec.end(), cmp);
    
    • 如函数cmp就是一个谓词(返回能作为条件的bool值,能调用cmp(a, b))
    • 接受谓词参数的算法对输入序列中的元素调用谓词,如cmp(vec.begin(), vec.begin()+1)
    • 接受谓词参数的sort版本用这个谓词代替<来比较元素

    3. 一元谓词&二元谓词:标准库算法或使用一元谓词或使用二元谓词,一元谓词意味着该谓词只能接受一个参数

    • 根据算法接受一元谓词还是二元谓词,我们传递给算法的谓词必须严格接受一个或两个参数

     

    二、lambda表达式

    1. 放弃谓词,改用lambda表达式

    • 当算法要求使用X元谓词,而实际使用到的谓词接受的参数个数大于X时,我们需要使用lambda表达式
    • 如find_if接受一个一元谓词,故传递给find_if的可调用对象必须接受单一参数

    2. 可调用对象:可以对该对象使用调用运算符,如e是一个可调用对象,则我们可以编写代码e(args)

    • 可调用对象包含:函数、函数指针、lambda表达式、重载了函数调用运算符的类

    3. 形式:[捕获列表](参数列表) -> 返回类型 { 函数体 }

    • 一个lamda表达式表示一个可调用的代码单元,我们可以将其理解为一个未命名的内联函数
    • 可以忽略参数列表和返回类型,但必须永远包含捕获列表和函数体,auto f = [] { return 24; };,f就是可调用对象,可调用它:cout << f() << endl;
    • lambda默认返回void类型,除非其函数体只有一条return语句
    • lambda不能有默认参数

    4. 捕获列表

    • lambda只能在其函数体中使用它在捕获列表中捕获的局部变量,但可直接使用局部static变量和全局变量

    5. lambda是类?

    • 定义一个lambda时,编译器生成一个与lambda对应的新的(未命名的)类类型
    • 当向一个函数传递一个lambda时,同时定义了一个新类型和该类型的一个对象(传递的参数就是此编译器生成的类类型的未命名对象)
    • 如用auto定义一个用lambda初始化的变量,就定义了一个从lambda生成的类型的对象,如auto f = [] { return 24; }
    • 从lambda生成的类都包含一个对应该lambda所捕获的变量的数据成员

    6. 值捕获

    • 前提:变量可以拷贝
    • 被捕获的变量是在lambda创建时拷贝,而不是调用它时拷贝

    7. 引用捕获

    • 形式:auto f = [&v] { return v; }
    • 在lambda函数体内使用此变量时,实际上使用的是引用所绑定的对象
    • 采用此方式,就必须确保被引用的对象在lambda执行的时候是存在的

    8. 隐式捕获

    • 让编译器推断要使用哪些变量
    • 形式:[=]或[&],前者是值捕获,后者是引用捕获

    9. 可变lambda:修改捕获列表中变量的值

    • 值捕获:使用mutable关键字
    • 引用捕获:引用指向的要是一个非const类型

    三、标准库bind函数

    1. 放弃lambda表达式,改用函数?

    • 在很多地方使用相同的操作,那么应该定义一个函数;此外,一个操作如果需要很多语句才能完成,则应使用函数
    • 因为函数接受的参数个数可能有悖于算法,所以就要着手解决该问题

    2. bind函数

    • 定义在头文件functional中
    • 可以将bind函数看作是一个通用的函数适配器,它接受一个可调用对象,生成一个新的可调用对象来“适应”原对象的参数列表

    3. bind调用的形式:auto newCallable = bind(callable, arg_list);

    • newCallable是一个可调用对象,当我们调用newCallable时,newCallable会调用callable,并传递给它arg_list中的参数,如:
    bool check_size(const string &s, string::size_type sz)
    {
    	return s.size() >= sz;
    }
    //check6接受一个string类型的参数,并用此string和值6来调用check_size 
    auto check6 = bind(check_size, _1, 6);
    
    • 此bind调用只有一个占位符,表示check6只接受一个参数,占位符为_1,表示check6的此参数对应check_size的第一个参数
    • 于是,bool b1 = check6(s)会调用check_size(s, 6)

    4. 参数绑定

    	auto wc = find_if(words.begin(), words.end(), bind(check_size, _1, sz)); 
    • 此bind调用生成一个可调用对象,将check_size的第二个参数绑定到sz的值
    • 即bind调用生成的新的可调用对象只接受一个参数,即它是一元谓词,但它的参数列表中有参数绑定到局部变量

    5. 名字_n都定义在一个名为placeholders的命名空间中,而这个命名空间本身又定义在std命名空间内

    6. bind的参数:可以用bind绑定给定可调用对象中的参数或重新安排其顺序

    	//g是一个有两个参数的可调用对象 
    	auto g = bind(f, a, b, _2, c, _1);
    
    • 此bind调用生成一个可调用对象g,它有两个参数,传递给它的第一个参数绑定到_1,第二个参数绑定到_2
    • 当我们调用g时,其第一个参数将被传递给f作为f作为最后一个参数,第二个参数将被传递给f作为第三个参数

    7. 其他

    • bind拷贝其参数,故我们不能用bind代替捕获引用的lambda
    • 如果我们希望传递给bind一个对象而又不拷贝它,就必须使用标准库ref函数
    	for_each(words.begin(), words.end(), [&os, c](const string &s) { os << s << c; });
    	for_each(words.begin(), words.end(), bind(print, ref(os), _1, ' '));
    
    • 函数ref返回一个对象,包含给定的引用,此对象是可以拷贝的
    • 标准库还有一个cref函数,生成一个保存const引用的类
  • 相关阅读:
    leetcode231 2的幂 leetcode342 4的幂 leetcode326 3的幂
    leetcode300. Longest Increasing Subsequence 最长递增子序列 、674. Longest Continuous Increasing Subsequence
    leetcode64. Minimum Path Sum
    leetcode 20 括号匹配
    算法题待做
    leetcode 121. Best Time to Buy and Sell Stock 、122.Best Time to Buy and Sell Stock II 、309. Best Time to Buy and Sell Stock with Cooldown 、714. Best Time to Buy and Sell Stock with Transaction Fee
    rand7生成rand10,rand1生成rand6,rand2生成rand5(包含了rand2生成rand3)
    依图
    leetcode 1.Two Sum 、167. Two Sum II
    从分类,排序,top-k多个方面对推荐算法稳定性的评价
  • 原文地址:https://www.cnblogs.com/xzxl/p/7818352.html
Copyright © 2011-2022 走看看