首页 > 编程语言 >C++11 -- 匿名函数(lambda 表达式)

C++11 -- 匿名函数(lambda 表达式)

时间:2022-09-19 09:13:55浏览次数:93  
标签:11 map cnt return 函数 -- C++ int lambda

0. 一道题目引入

关于sb力扣定义外部函数和变量报错这件事

最初我定义了一个 \(cmp\) 函数用来对 \(vector\) 排序,和一个全局变量 \(unordered\_map\) 用来记录元素个数。
但是 \(sb\) 力扣报我错,我也不知道为啥!于是我查看了官方题解 我是sb,这种题还要看题解,发现官方题解是这样定义自己的 \(cmp\) 函数的:

sort(nums.begin(), nums.end(), [&](int a, int b) ->bool{
    if(cnt[a] == cnt[b])    return a > b;
    return cnt[a] < cnt[b];
});

没错!它直接在 \(sort\) 里面定义了一个 “没有函数名字” 的 \(cmp\) 函数。
把 "\(cmp\) 函数" 单独拿出来就是:

[&](int a, int b) -> bool{
    if(cnt[a] == cnt[b])  return a > b;
    return cnt[a] > cnt[b];
}

于是我就上网 \(goole\) 了一下这个语法,才知道这是 \(C ++ 11\) 的新特性:\(lambda\) 函数,又叫做匿名函数,\(lambda\) 表达式。

这是AC代码:

class Solution {
public:
    vector<int> frequencySort(vector<int>& nums) {
        int n = nums.size();
        if(!n)  return nums;
        unordered_map<int, int> cnt;
        for(auto &x : nums)
        {
            cnt[x] ++ ;
        }
        
        sort(nums.begin(), nums.end(), [&](int a, int b) ->bool{
            if(cnt[a] == cnt[b])    return a > b;
            return cnt[a] < cnt[b];
        });
        return nums;
    }
};

1. lambda 表达式介绍

先上语法:

auto val = [captures](args) -> return_type {
	...
}

val : 对象名
[captures] : 捕获作用域内变量
(args) : 匿名函数的参数列表
-> return_type : 函数返回值类型
          如果 lambda 函数没有传回值(例如 void),其返回类型可被完全忽略。
{...} : 函数实现体

一个简单的例子:通过匿名函数实现 x+y

int main()
{
    int x = 1, y = 2;
    auto f = [&]() -> int {
	    return x + y;
    };
    
    cout << f() << endl;
    return 0;
}

我个人认为,这个语法更应该叫做 \(lambda\) 表达式,而“匿名函数”不是非常合适,因此它并是真的完全“匿名”,它也是有名字的。
在上面的例子总,我们定义了一个 “函数” \(f\)[应该是个函数吧,毕竟需要通过()调用],\(f\) 不就是它的名字吗?
但为啥还要说“匿名”呢,我觉得,\(f\) 并不能理解为一个函数的名字,它应该理解为一个“对象”的名字,\(f\) 是一个“对象”,只不过这个对象的内容不是一个 \(int\),也不是一个 \(char\),而是一个“没有名字的函数”罢了。

2. lambda 表达式的闭包

在 \(Lambda\) 表达式内可以访问当前作用域的变量,这是 \(Lambda\) 表达式的闭包(\(Closure\))行为。 与 \(JavaScript\) 闭包不同,\(C++\) 变量传递有传值和传引用的区别。可以通过前面的 \([]\) 来指定:

[]      // 沒有定义任何变量。使用未定义变量会引发错误(即不能引用函数自己定义的参数之外的变量)。
[x, &y] // x以传值方式传入(默认),y以引用方式传入。
[&]     // 任何被使用到的外部变量都隐式地以引用方式加以引用。
[=]     // 任何被使用到的外部变量都隐式地以传值方式加以引用。
[&, x]  // x显式地以传值方式加以引用。其余变量以引用方式加以引用。
[=, &z] // z显式地以引用方式加以引用。其余变量以传值方式加以引用。

另外有一点需要注意。对于 \([=]\) 或 \([&]\) 的形式,\(lambda\) 表达式可以直接使用 \(this\) 指针。但是,对于 \([]\) 的形式,如果要使用 \(this\) 指针,必须显式传入:

[this]() { this->someFunc(); }();

更多关于语法的细节

3. lambda 值引用 map/unordered_map 的一个坑

简单来说,在 \(lambda\) 的捕获列表里面捕获 \(map/unordered\_map\) 要么是引用捕获,要么使用 \(at()\) 来获取容器内的元素。
这是因此当捕获为 \(map/unordered\_map\) 的值时,\(lambda\) 表达式会默认转换为 \(const map/const unordered\map\)。
之所以会发生这种转换,是因为 \(map/unordered\_map\) 的一个特性:当我们引用一个关键字 \(key\) 的使用,如果这个关键字不存在则进行插入,也就说会修改容器。
另外,注意使用 \(at()\) 去获取容器内元素,如果 \(key\) 不存在,会报错:\(out\_of\_range\),因为我们引用了一个容器中不存在的 \(key\)。
这也就证明了使用 \(at()\) 不会插入关键字,也就不会修改容器。

再来测试一下这个特性:当我们引用一个关键字 \(key\) 的使用,如果这个关键字不存在则进行插入,也就说会修改容器。

int main()
{
    unordered_map<int,int> m;
    m[1] = 1;
    cout << m.size() << endl; // output:1
    int x = m[2];
    cout << m.size() << endl; // output:2
    
    return 0;
}

可以发现,我们仅仅只是引用了 \(m[2]\),但容器的 \(size\) 多了一个。

具体的可以看一个博客:C++11 lambda表达式不能捕获map/unordered_map值

标签:11,map,cnt,return,函数,--,C++,int,lambda
From: https://www.cnblogs.com/ALaterStart/p/16706576.html

相关文章

  • Spring(三):IoC的本质
    一、图例  对照上面的图,我们回想上一篇中几个代码的实现,在没有set注入之前,代码运行完全由Service层控制,用户没有选择权,选择权在程序员手中;但是使用set注入之后,用户可......
  • AOP实现系统告警
    工作群里的消息怕过于安静,又怕过于频繁一、业务背景在开发的过程中会遇到各种各样的开发问题,服务器宕机、网络抖动、代码本身的bug等等。针对代码的bug,我们可以提前预......
  • ES6 对String做的常用升级优化
    ES61.let有什么用,为什么有了var还要使用let在ES6之前,声明变量只能用var,var声明变量有很多不合理的点,准确的说是因为ES5中没有块级作用域是很不合理的,甚至可以说是一门语......
  • 44. SP数据存储
    44.SP数据存储44.1数据存储是什么创建一个新工程数据保存到APP本身。44.2数据存储有哪些SP、SQLite【原生】、Room【更简洁】记住用户名、自动登录、看了书......
  • 45. SQLite
    45.SQLite45.1SQLite介绍SQLite关系型数据库。嵌入式的数据库,体积小,功能强大,几十kb。在Android平台上,集成了一个嵌入式关系型数据库—SQLite,SQLite3支持NULL、INTEGER......
  • Day01学习java的第一太难
    Makdown学习标题字体helloworld!helloworld!helloworld!分割线 引用我是你爹图片超链接我是你亲列表abcabc表格名字性别生......
  • 获取图片信息
      引入依赖jar<dependency><groupId>com.drewnoakes</groupId><artifactId>metadata-extractor</artifactId><version>2.8.1</version></dependency>  ......
  • java基础知识点
      这个是数据结构,在不同地方都用到了,在不同集合中用到  各种集合和组成结构......
  • springboot中解析JSON参数
    解析psot请求中的JSON参数Map<String,String>attrMap=newHashMap<String,String>();BufferedReaderstreamReader=null;try{streamReader=newBufferedRead......
  • 环境创建和查看,等命令
    condainfo--envs:输出中带有【*】号的的就是当前所处的环境创建condacreate--nameyourEnvpython=2.7 condalist:看这个环境下安装的包和版本condainstallnum......