铁路桥刚刚开工,待完善,下一步计划更新一篇博客,文章引用本站内部链接。

盖茨比有多了不起?

盖茨比有多了不起?

我没看过《了不起的盖茨比》的原著,只是常常在各种地方看到人们引用其中的名言——

每逢你想要批评任何人的时候,要记住,这个世界上并非所有人,都拥有你所拥有的那些优势。

正是这句话,激起了我对这个作品的兴趣,于是我看了小李子主演的同名电影。

然而,看完整部电影后,我并没有感受到之前引用的那句话的震撼,反而觉得这个故事有些奇怪,让人有些摸不着头脑。

直到有一天,我偶然听到杜老师的公开课,才恍若大梦初醒。

原来,盖茨比的确“了不起”,他有明确的目标,有切实的手段,并为之不懈奋斗,且在一部分运气的加持下,他也确实达到了某种意义上的成功。

但他仍然不够“了不起”。盖茨比没有意识到自己的独特之处,他不相信自己值得被尊重和认可,反而急切地渴望得到别人的认同。然而,那些别人,往往远不如他自己更具价值和优势。

在这一点上,歌手王菲无疑显得更加“了不起”。她20年前在台湾金曲奖的获奖感言中提到过——

我会唱歌,我知道;对于金曲奖评委对我的肯定,我也给予充分的肯定。

无论得奖与否,王菲始终保持着对自己价值的认同,从不依赖外界的评价来确认自己的存在意义。

顺便一提,杜老师的文学讲解充满了人文关怀与深邃的思考。如果大家对《了不起的盖茨比》不太熟悉,不妨看看她对《祝福》的解读,或者了解她眼中的祥林嫂.

漫谈pair

引子

故事的开端,是一位朋友将老代码迁移到C++11标准时遭遇了编译错误。他的代码大致如下:

1
2
3
4
5
extern void fun(std::pair<int ,int>);

int v = 5;

fun(std::make_pair<int,int>(v,6));

编译器抛出了错误:

cannot bind rvalue reference of type ‘int&&’ to lvalue of type ‘int’

错误的原因显而易见,std::make_pair<int,int>需要int &&作为参数,而v是一个左值,无法匹配。一个简单的解决方案是:

1
fun(std::make_pair<int&,int>(v,6));   //用int&替代int

这样写确实可以通过编译,fun函数也确实接收了一个pair<int ,int>对象,没有bug,可正常工作。不过,这背后牵涉的std::decay引用折叠等知识,并不是本文的重点,这里就不详细展开了。

聊聊pair

以上是个引子,基于这个引子,本文主要想聊聊std::pair<int, int>
std::pair<int, int>是个模板类,属于stl的一部分,它拥有两个数据成员firstsecond,并且提供了各种行为良好的构造函数,以及默认的<,==>等操作符。

让我们通过两个代码片段来比较一下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// code segment A
stuct PersonInfo
{
int age;
int salary;
}
PersonInfo ZhangSan; //张三的信息
ZhangSan.age = 30;
ZhangSan.salary = 3000;

// code segment B
std::pair<int, int> LiSi; //李四的信息
LiSi.first = 40; //李四的年龄
LiSi.second = 4000; //李四的月薪

显然,代码A比代码B要易理解,agesalary变量名精确地表示了自身的含义,无需额外注释。因此,大多数情况下,我们不建议大家用pair来替代包含两个数据成员的结构体。

那么,pair应该在什么场合使用呢?

  • 第一种情况是,我们使用了第三方代码时,接口要求我们使用pair,例如在使用各种stl模块时。
  • 第二种情况是,我们编写代码时,还不知道这个结构体将用来存储什么样的具体数据,例如,当我们编写自己的容器模板代码时。
  • 第三种情况是,我们想对结构体进行“比较大小”的操作,但又不想自己重载对应的操作符。

第一种情况下,我们不得不用pair;第二种情况下,我们应该主动选用pair;而在第三种情况下,则应尽量避免使用,除非我们确信代码的混乱程度在可接受的范围内。

从pair到make_pair

回到引子的问题吧,那个编译问题是由make_pair函数引起的。

让我们来看看make_pair的引入。

考虑下面的代码(假设fun是一个接收特定pair参数的函数):

1
2
vector<int> vi;
fun(pair<vector<int>::const_iterator,vector<int>::const_iterator> (vi.cbegin(),vi.cend()));

这段代码中<vector<int>::const_iterator,vector<int>::const_iterator>的出现让人觉得冗长,且掩盖了重要信息。程序员们希望代码能更简洁,于是make_pair应运而生,代码可以写成:

1
2
vector<int> vi;
fun(make_pair(vi.cbegin(),vi.cend()));

相比之前冗长的版本,修改后的代码更容易让人抓到重点,fun函数接收了一个pair参数,其两个元素分别是vi.cbegin()vi.cend()

make_pair的演化

让我们来看看make_pair的演化历程。

最早,即上个世纪C++刚刚标准化时,make_pair的接口形式是:

1
2
3
template<class T1, class T2>

pair<T1, T2> make_pair(const T1 &x, const T2 &y);

参数类型是const &,这样可以避免不必要的拷贝。

然而,这样的定义存在问题。例如,make_pair("abc",3)这样的代码就会无法通过编译,因为T1类型会被推断成const char[4](而不是const char *),而pair<const char[4],int>是不合法的,因为first(T1)类型无法拷贝。

按我们的直觉,make_pair("abc",3)应该返回pair<const char * ,int>。我们的直觉重要吗?当然重要!因此,在本世纪初,C++03标准对make_pair的接口做了一个补丁,修改为:

1
2
3
template<class T1, class T2>

pair<T1, T2> make_pair(T1 &x, T2 &y);

这样,make_pair("abc",3)可以通过编译了,尽管理论上的代码执行效率有所降低。至于理论上的效率变低,标准还为此特意给编译器优化开了”绿灯“(这又是另外一个故事了,此处不再展开)。

随着时代的发展,程序员们对效率提出了更高的要求。于是,C++11带来了着右值引用等特性。

make_pair顺势而为,再次变更了接口,兼顾了“直觉”和效率,变成了:

1
2
template< class T1, class T2 >
pair</*V1*/, /*V2*/> make_pair( T1&& x, T2&& y );

这样,理论上大部分代码都更高效了。不过,极少数人可能会觉得困惑。还记得我们引子部分的代码吗?我再贴一次:

1
2
3
4
5
extern void fun(std::pair<int ,int>);

int v = 5;

fun(std::make_pair<int,int>(v,6));

这段代码在上个世纪是没有问题的,在C++11之前也没有没问题,但更新到C++11之后,就面临编译错误,就必须修改。

当然,这是代码本身的问题,要怪就只能怪程序员写得不好,不能怪标准的变化。具体原因我稍后再详细说明。

从make_pair回归pair

让我们回到最初引入make_pair的目的:简化代码,使其更易写易读。

例如让下面的代码变得更好读:

fun(pair<vector<int>::const_iterator,vector<int>::const_iterator> (vi.cbegin(),vi.cend());

当我们能使用C++17标准时,由于模板构造函数可以自动推导参数,我们不再需要借助make_pair,可以直接写成:

1
fun(pair(vi.cbegin(),vi.cend()));

这样,代码干扰更少,更清晰,也更接近自然语言的表达。

是的,当我们的编译器支持C++17标准时,make_pair可以逐渐退出历史舞台了。make_pair很好,但我们已经有了更好的替代方案。

关于代码的好坏

回到引子部分的代码,为什么我会说“要怪就只能怪程序员写得不好,不能怪标准的变化”呢?

因为写那段代码的人,他违背了make_pair被创造的初衷,make_pair的出现,本来就是为了减少一次模板参数的重复,而他却显式地写了一遍模板形参!

让我们对比一下,一个追求代码整洁有的程序员,哪怕在上个世纪,也会写出这样的代码:

1
2
3
4
5
6
extern void fun(std::pair<int ,int>);

int v = 5;

//fun(std::make_pair<int,int>(v,6)); //有代码品味的程序员,会本能地发现这个“脱裤子放屁”的味道
fun(std::make_pair(v,6)); //这才是简洁的代码,而且从c++98开始,直到20多年后的今天,都不会有编译问题

一个追求代码质量的人,即使不及时跟进语言标准的变化,也不会有太大问题。因为在大多数情况下,标准的进化会让原本就很好的代码运行得更快更好。

至于那些随着标准进化而无法编译的代码,通常它们本身就很糟糕。

语言标准的演进,本质上是在奖励优雅的代码(以及写代码的思路),而不是为糟糕的代码提供避风港。写出简洁、清晰、符合本意的代码,才是程序员应有的追求。

第二张脸

前两天,我和妈妈跨洋视频通话。她瞧了瞧我刚剪的头发,皱着眉头问:“怎么剪得这么短?”那语气,倒不是责备,只是透着些惋惜。言下之意,无非就是——不好看。

我剪的是圆寸,在我妈那个年代人的眼里,这种发型仿佛是刚出来的服刑人员,还没来得及让头发长出来。显凶,像个不好惹的人——当然,不好看。

今天在机场廊桥上准备登机时,突然有一位地勤的老太太迎面走来。她冲我微笑,轻声说道:“谢谢你刚才给小朋友让座。”这时我才想起,不久前,这位地勤阿姨指引两个无监护人陪伴的小朋友上飞机,当时候机室里已没有连续的两个空座椅。我看到孩子们快走到跟前,就默默起身腾了个位置。她看到了,记住了我这个“凶巴巴”的发型。可她大概并不觉得我真凶吧,否则也不会特意来微笑道谢。

听说,每个人都有两张脸。第一张脸,是外貌,是天生的模样;而第二张脸,却复杂得多——它藏在气质里,可透过谈吐展现,映在过往的成绩与留下的作品中。第二张脸,会悄悄地修饰甚至改变第一张脸在人心中的模样。

体育界常有“美女”的说法,但倘若抛开具体成绩,那些令人惊艳的美丽容颜,未必总是那么引人注目。比如游泳运动员刘湘,多年前她就说:“我之前也长这样,只是因为比赛成绩好,大家才会关注我。”

再比如台湾的摇滚歌手伍佰。若单看照片,不少不知情的小朋友可能会惊呼:“他看起来像坏人!”可若是放在一个听过《白鸽》或《树枝孤鸟》的乐迷面前,同样的照片或许会引来一句“这才是真正的摇滚诗人,酷毙了!”

内在的美,不仅是内在的事。它还能反过来影响别人对外在的评价。

纯粹的外在美,是易凋的花,开得艳,却难久留。而内在的美更像一坛老酒,时间越久,愈发醇香。

那么,我这副“很不好惹”的模样,怎么办?其实,也没什么好担心的。那些愿意花时间了解我的人,自然会透过我的作品知道我,明白我并不坏。而至于那些没耐心的人,我也不需要他们的认可。

当然,第一张脸也不是全无意义。如果妻子嫌弃我满脸油光,我还是会立刻去洗脸。毕竟,对于那些最了解我“第二张脸”的人,若他们对我的外貌还有意见,那我想,我也该听一听,改一改了。

近期的拖延症

最近一周我休假了。休假的好处在于,可以多做些自己喜欢的东西,少去理会那些别人期望我去做的事情。

平日里,与自己潜意识对话时,总是充斥着“我应该”、“我不得不”这样的词句。而在假期,这样的内心对白变得温和、主动,多半是用“我想”、“我要”的语气。

假期刚开始时,我给自己列了一长串的事务清单,比如看数学书、写代码、逛图书馆、玩电子游戏等等。假期里,我依然保持着每天早晨六点起床,晚上十一点前休息的规律生活,清单里的大部分项目也在稳步推进,一项项从“待做”变成了“已做”。然而,唯独一件事成了例外——“写一篇散文,更新到自己的博客上”。

在假期的第一天,我就给自己定下了写文章的计划。本以为这会是件轻而易举的事,也许是第一天晚上,也许是第二天清晨,花个半小时到一个小时便能完成。然而,如今假期已过去三分之二,我的文章才刚刚开了个头,也就是大家现在看到的这段文字。

写文章对我来说,本该是很自然的事。平日里遇到事情、想到什么,总会有种不吐不快的冲动,摊开纸笔或打开电脑,文字很快便能流畅地写下来。

不过,我或许记错了。刚才提到,写文章是自然的事,而实际上,我已经有十多年没好好写过东西了。十多年前的我,写文章确实很顺手,很习惯,不管写得如何,至少过程中很少遇到阻碍。而这些年,我渐渐放下了写作。为什么呢?我也没细想过原因。只是生活在变化,不知不觉中,一些习惯也随之消失了。

是的,情况大概就是这样——写作是我曾经的老习惯,这个习惯突然中断了。十多年后,当我试图重新拾起它时,竟在开始的第一步就犯了拖延的毛病。

如前面提到的,写代码、看数学书等事情,并非轻松之事,但我一点儿也没拖延。为什么唯独在写作上,我迟迟无法下笔?

拖延,或许是完美主义在作祟,总觉得条件还不够成熟,自己还没准备好,担心不能一气呵成地写出理想的作品,于是一直在等待。但我也明白,做了总比不做好。现实中的平平无奇,总好过只停留在幻想中的完美。只存在于空想中的金碧辉煌并没有任何意义。正因如此,我在写代码时毫无犹豫。

拖延,尤其是对“重拾某事”的拖延,也许是因为害怕体验到“退步”的感觉。做得还不如从前,别人会怎么看,自己面子又在哪里?但我也知道,“三天不练手生”是自然规律,退步是可以理解的,无需羞愧。再者,有些“退步”只是意味着我们走出了舒适区而已,短期的退步往往是长期进步的必要过程。因此,在玩游戏时,我也没有拖延。尽管因为输入设备的更换,我总是输,现在的操作水平还不及五年前的一半。

拖延,或许还因为害怕过程的艰难。有得选择,又何必自讨苦吃?但我也知道,困难的事情随着经验的积累,会逐渐变得容易、变得习惯。因此,我去参加网上背英语单词游戏时,根本不在乎它难不难。这个赛季结束了,还有下一个赛季。从自我感觉上来说,每个赛季总比上一个赛季轻松一些。

为什么呢?为什么这篇文章还是写得那么慢呢?明明道理我都懂,在其他事情上也做得不错,可为什么偏偏在写作上栽了跟头呢?

知易行难。道理都懂,可做起来却总是那么艰辛。如果做起来轻松,那也许只是因为遇到的阻力不够大吧。相比其他事情,我对写文章这件事更加在意,因此思想包袱也更重。我非常希望自己能把文章写好,对自己过去的水平也怀有不切实际的滤镜,所以,拖延才会比其他事更甚。

其实,写文章是件很好的事情。同一个选题,同一个思路,完全可以写出多个版本的文章。这一篇不满意,那就重头再写一篇。写文章不同于炒菜,炒菜若炒得不好,材料就浪费了;而文章的“材料”并不是耗材,底层的想法和背景,可以在不同版本中反复使用,甚至可以在多篇文章中反复使用。因此,文章不满意,重写便是,没有所谓浪费选题或浪费思路的情况。写不好,代价不大。不用怕。

先把写作的习惯拾回来吧。我相信自己会比过去的自己更好,哪怕暂时不行,过一段时间也是可以期待的。

我回来了

上周,我给自己买了三个PC游戏。正版的。这些游戏,我曾经玩了多年。如今遇到一个促销的机会,我终于补票了。

我有多久没玩它们了呢?好久了。时间需要按年算。细想起来,不仅仅是游戏,还有码字、记录感想、看叙事文学,还有写与工作无关的代码等等,这些都曾经是我生活的一部分,而这些活动,如今却变得陌生而遥远。

那是我多久之前的样子呢?大约十多年前吧。

那之后的我,似乎逐渐走向了生活的另一个面貌,责任和担当成为了日常的一部分。忙碌和变化,使得这些曾经的乐趣逐渐被掩盖。我几乎没有时间去回望,旧日的兴趣也在生活的洪流中被渐渐淹没。

也许“抛弃”并不准确。它更像是一种自然的转变,没有仪式感,也没有告别的时刻,只是随着岁月的流逝,自然地远离了曾经的简单乐趣。

近期,生活中出现了些许间隙。我开始重新拥有对未来的思考,有了一些学习的冲动,并会因写代码而兴奋,甚至失眠。这些活动,与重拾的游戏一起,似乎将我带回了那段熟悉的日子。

此刻,我在心里对自己低声说道:“欢迎回来”。回到曾经的轨道上,重新望见以前的目标,看清脚下的路。

这是一种温暖而熟悉的感受,仿佛重回那个真实的自我。

开篇

这些年,生活中那些寻常的俗事,让我心生疲惫。尽管心中怀揣梦想,却常常被迫追求所谓的成功,或者去符合社会的期待。这些逐利的事情并未让我感到满足,反而让我愈发迷茫。

近日,状况似乎有了好转。我终于得以抽身,开始做一些自己真正喜欢的事情,比如建立一个小小的博客站点。虽然它尚且简陋,但这是一个新的开始,是我重新掌控时间的标志。

站在这个新起点上,回顾过去的困顿,心中不禁涌起一阵激动。如今,我终于可以自由支配时间,这真是难以言喻的欣喜。希望未来的日子里,生活能变得更加丰富多彩。

  • Copyrights © 2024-2025 刘清
  • 访问人数: | 浏览次数: