MoreRSS

site iconTimothy修改

四川人在新加坡,热爱编程和DIY。godns作者。微软MVP。
请复制 RSS 到你的阅读器,或快速订阅到 :

Inoreader Feedly Follow Feedbin Local Reader

Timothy的 RSS 预览

我的 AWS Certified Machine Learning – Specialty 认证之旅

2025-04-19 00:53:23

最近通过了 AWS Certified Machine Learning – Specialty 认证,再此分享一下我的备考经验和考试心得,希望能帮助到正在准备这项认证的朋友们。

时间线和准备方法

我的认证之旅从三月初开始,当时在 Udemy 上寻找相关课程。还好有公司的商业版Udemy订阅,可以免费学所有课程。利用周间下班的时间和周末的一些时间,到三月中下旬,我已经完成了课程学习并开始做习题练习。 然后我随即预约了三月底的考试,这给了我一个集中的准备窗口。

在线课程学习

Udemy 的课程为理解 AWS 机器学习生态系统提供了坚实的基础。课程涵盖了重要的服务,如 SageMaker、Comprehend、Rekognition 和 AWS与机器学习相关的各种服务,以及基本的机器学习概念和最佳实践。实践实验室对于获得这些服务的实际操作经验特别有帮助。

刷题经验

看过Sample Question后,你才会发现,只看视频课程是远远不够的。完成课程后,我专注于从两个主要来源练习题目:

  • Udemy 课程附带的样题
  • 网上找到的额外练习题

这些练习题对于识别知识盲点和需要额外学习的领域非常有价值。它们也帮助我熟悉了考试格式和问题风格。另外,对于做错的题目,我会仔细分析错误原因,并在网上查找相关资料进行补充学习。另外,AI工具也可以帮助你理解一些陌生的概念,有不懂的知识点就多问AI,让它帮你解释和举例,省去了很多时间。

预约与参加考试

考试我倾向于选择线下的,预约了一家离我家较近的 Pearson VUE 考试中心。考试中心的工作人员非常友好,整个过程都很顺利。

  • 级别: Specialty
  • 时长: 180 分钟(3 小时)
  • 费用: 300 美元 (还好是公司出钱)

考试的题目是多选题和单选题的组合,涵盖了 AWS 机器学习的各个方面,包括模型训练、数据处理、部署和监控等。考试的题目有时会很复杂,涉及到多个 AWS 服务的组合使用。题目通常会给出一个场景描述,然后要求你选择最合适的解决方案或服务。

值得注意的是,考试题目是全英文的,所以需要一定的英语阅读能力。考试时,考生需要在 3 小时内完成 65 道题目,这意味着每道题目的平均时间大约是 2 分钟。虽然时间看似充足,但有些题目需要仔细阅读和分析,所以时间管理非常重要。

考试总分为 1000 分,750 分及以上为通过。

在考试注册网站上有一个小福利,如果你的母语不是英语,可以申请额外的 30 分钟时间。

考试心得

考试本身有几个挑战:

  1. 许多题目都有冗长的场景描述,需要仔细阅读以识别关键信息
  2. 一些多选题很有技巧性,需要密切关注细节
  3. 时间管理至关重要:3小时起初看似充足,但复杂题目需要大量阅读时间
  4. 考试总时长3小时,对于体力和注意力都是一个挑战

有效的应对策略:

  1. 对于冗长的题目,先集中精力识别关键需求和约束条件,然后再查看答案选项
  2. 对于具有挑战性的问题,使用了”标记”功能,稍后再回来处理
  3. 对于多选题,先仔细排除明显不正确的答案
  4. 预留至少20-30分钟的时间进行最终复查已标记的问题

查询成绩

完成考试后,并不会直接展示成绩,官方承诺会在48小时内通知成绩。其实就在考试过后的第二天,就收到了邮件通知,成绩已经更新到AWS Certification Account上了。登录后查看成绩,750分以上就算通过了。 随后,收到了 AWS 认证的电子证书和徽章,可以在 LinkedIn 和其他社交平台上展示。

最后,得感谢公司免费的学习资源和认证机会,也感谢 Udemy 的课程帮助我顺利通过考试。希望我的经验能对正在备考的朋友们有所帮助!祝大家都能顺利通过认证,取得好成绩!

2024年的年终总结:转变与沉淀

2025-01-01 23:30:00

看着屏幕上的倒计时,我以相当赛博的方式在Youtube上看了世界各地直播的跨年烟花秀,转眼间,2024就这样结束了。若要用一个词来概括这一年,大概是”转变”吧 —— 工作的转变、生活节奏的转变,以及思维方式的转变。而在这些转变之中,我也在寻找着一种新的平衡与沉淀。

工作

六月的新加坡,依旧是那般湿热。6.21日是我在Shopee的最后一天,收拾着办公桌上零散的物件时,我不禁回想起五年前初来乍到的场景。从当初努力适应英文工作环境的新人,到如今习惯了一切的”老sailor”,时间的痕迹悄然留在工作邮箱里几千封往来的邮件中,以及Seatalk的各个群组中。记得刚来时,每次开会都要全神贯注地听着同事用英文讨论,生怕错过重要信息。而今天,会议室里的交谈早已不知不觉切换成了中文,曾经朝夕相处的其他同事也都已经离开。这种变化让我感慨:或许,是时候寻找新的挑战了。五年的时光,不长不短,却足以让一个人对环境产生眷恋,也足以让人萌生出探索未知的渴望。

其实早在2024年初,我就开始了新的求职之旅,也开始在LeetCode上刷题。整个求职季,我面试了3到4家公司,每一次面试都是一次宝贵的经历,让我看到了不同公司的文化与技术栈。终于在四月底,我收到了一个令人满意的offer,那一刻的喜悦让之前的所有付出都变得值得。于是,也顺带向Shopee递交了离职申请,开始了为期两个月的 notice period。

就这样,在最终结束了五年的旅程后,我开启了职业生涯的新篇章。新公司带给我的第一印象,是那种久违的多元文化氛围。会议室里又重新响起了各种口音的英文,团队里来自不同国家的同事让我仿佛回到了最初来新加坡时的感觉,而我,也成为了团队里唯一的中国同事。让我感到欣喜的是,老板在我入职不久就把一个重要项目交到了我手上。这种信任带来的不仅是压力,更多的是一种被重视的感觉。在新的环境里,我重新找到了那种初入职场时的干劲和期待。

阅读

工作环境的转变,也带来了生活节奏的改变。告别了在Shopee在职读硕士时紧绷的步调,我终于有了更多业余的时间。今年在微信读书上完成了13本书的阅读,从技术书籍到文学作品以及个人传记,每一本都让我获得了不同的收获。现在想来,这可能是毕业后最重要的改变之一: 在工作之余找回了持续学习的节奏。那些宁静的阅读时光,成为了我生活中不可或缺的一部分。

技术书籍方面,读完了《搞定系统设计:面试敲开大厂的门》,这本书对于系统设计的基础知识和面试技巧有很好的总结。另外,还读了《黑客与画家》,让我对技术的发展有了更深的理解。

在文学作品方面,读了余华的两本作品:《活着》、《第七天》,还读了《杀死一只知更鸟》,每一本都让我感受到了作者的情感与思考。

个人传记方面,读了《李光耀观天下》,还重读了《乔布斯传》和《硅谷钢铁侠》。这些传记让我看到了不同领域的成功者的人生轨迹,也让我对自己的职业生涯有了更多的思考。

另外,还读了乔治·奥威尔的《1984》和《动物庄园》,这两本书对于当代社会的政治与社会问题有着深刻的思考,也让我对人性有了更多的认识。

其他的书籍还包括《全情投入》,《我的二本学生》,等。

开源项目

如果说工作与阅读是一种向内的沉淀,那么继续投入开源项目则是我探索世界的一种方式。今年还有一个重要的改变是告别了前公司配发的2019款MacBook Pro,利用新公司的补贴,直接入手了MacBook Pro M3。还记得第一次运行编译项目时,那种几乎察觉不到风扇声的安静,和以往等待编译时的轰鸣形成了鲜明的对比,ARM芯片果然名不虚传!新的设备不仅带来了效率的提升,更激发了我在技术探索上的热情。

今年除了继续维护 GoDNS 这个老朋友,我还用Rust开启了两个新项目。选择Rust不仅是因为它的技术特性,更是因为学习一门新语言的过程,总能带来思维方式的革新。当 knock-rstcping 这两个项目逐渐成型时,那种创造的喜悦感让我想起了最初写代码时的兴奋。在开源社区里,每一个Star、每一个Issue,都让我感受到代码背后的连接与互动。

旅行

六月初,我一次性清空了在Shopee剩余的所有十几天年假,回成都看望了家人。新公司入职后又喜提了12.5天年假,于是,年末的旅程带我再次重返东京,也让我初访槟城。

在东京的街头,我不再像去年那样匆忙地打卡景点,而是更多地去感受这座城市的氛围。在秋叶原的电器店里流连,在涩谷的十字路口观察来来往往的人群,在居酒屋吃当地的烧鸟和烤肉,这些细节让我对这座城市有了更深的理解。除此之外,还抽空二刷了镰仓,去看了冬天的富士山,最后还去参观了三鹰之森吉卜力美术馆。

东京都厅展望台的夜景

上野公园的红叶

明治神宫

从涩谷SKY上俯瞰城市景观

一澜拉面

镰仓江之岛上的寺庙

镰仓的海边

镰仓–长谷寺

富士山下, 天气不错,山顶的雪清晰可见。让我想起了陈奕迅的那首歌:谁能凭爱意要富士山私有…

三鹰之森吉卜力美术馆

而槟城之行,则是一场穿越历史与美食的探索,殖民时期的建筑与街边的小贩,编织出一座城市独特的底蕴。每一次的旅行,都像是给自己按下了暂停键,让匆忙的生活得以片刻的沉淀。

槟城的海边

娘惹博物馆

被动收入

如果你读过我之前的一篇blog《聊聊被动收入与躺赚》,就知道我一直在做一些可以产生被动收入的个人项目。2024年,在被动收入这条路上,我也尝试着一些新的可能。给一个长期维护的个人项目加入Google广告是一个小小的尝试,却带来了意想不到的收获。这让我明白,有时候改变不必太大,重要的是持续地尝试和积累。我打算把这部分收入称为我的”Kopi Fund” (咖啡基金,Kopi是咖啡在南洋的叫法),用来支持我在喝咖啡和阅读上的一些小小花销。

总结

展望2025年,我希望能在这些改变中找到更多的可能性。继续在技术探索中保持热情,在阅读中沉淀思考,在旅行中开阔视野。期待在工作中有更大的突破,在开源社区贡献更多价值,也能在投资理财上有新的收获。重新深入学习美股期权将是明年的一个新挑战,也许就像今年的Rust学习一样,会带来一些意想不到的收获。

毕竟,生活就是在不断的转变中寻找平衡,在变与不变之间沉淀出自己的节奏。这大概就是2024年给我最大的领悟。在来新加坡的第六个年头,我依然在寻找着属于自己的生活方式,期待着每一种新的可能。

迟来的2023年终总结

2024-01-15 08:05:28

这是一篇迟来的2023年终总结。2023年底的时候出去旅游了,2024年初又回国了一次,所以这篇年终总结终究还是迟到了半个月。

工作

如果用一句话总结去年的工作,那就是:平淡无奇,甚至还变卷了一些。看着曾经并肩作战的同事,一个个都离开了,曾经开会的时候,默认的语言还是英语,如今开会都是默认直接中文了。年初由于组织架构的再次调整,我又被合并到了另外一个组,开始用C++开发线上的实时推荐系统。于是,我的技术栈也因此扩展成了React, Go, C++。在业余时间,我还顺便学习了一下现代C++,毕竟上次用传统的C++已经是N年前了。至于工作本身嘛,我只能说,走一步看一步,我还是先刷着LeetCode热热身吧。

学习

2023年这一整年,最值得令人高兴的事情是:我终于又毕业了!

从疫情开始的线上课程,到后疫情时代的线下课程,总共历时两年的时间,我又毕业了。回顾起当初去学院提交资料,领取学生卡的场景还仿佛就停留在昨天。这两年的时间里,做得比较有规律的事情,就是遇到有课程的时候,早上起来一大早去公司上班,下班后又马不停蹄地飞奔去学校接着上课。晚上回家还得卷一下当天课程的quiz,周末也得继续和小组的同学一起讨论项目,写report。除了假期的周末,平时是几乎没有周末的。这种忙碌而又充实的生活,让我觉得仿佛又回到了多年前的那个学生时代。

这种忙碌的生活一直重复了四个学期,最后一个学期伴随着最后两门的考试,最终到了2023年4月底。6月的一天早上,我跟往常一样在公司上班的时候,收到了学院的邮件,通知可以顺利毕业了。

7月,学校的毕业典礼,早上一场突如其来的大雨,导致路上堵车了40多分钟,本来以为是赶不上最后的毕业典礼了,没想到赶在毕业典礼开始的前几分钟顺利入场了。

有同事问我:本科毕业多年后,还有必要再去读一个master吗?对我而言,我觉得是有必要的。

当年因为家庭经济的原因,没有能出国深造的机会。本科毕业之后,我就开始在国内工作了,直到后来到了新加坡。没能出国体验留学,对我来说难免是一件比较遗憾的事情。后来从NUS毕业来公司的新同事那里了解到,原来国外的master是没有入学考试的,直接拿着本科成绩单和英语考试成绩申请就可以了。而且part-time的证书和full-time是一样的,学费也一样,就连平时上课都是一起上的。

于是,我就疯狂DIY走完了接下来的所有流程:考雅思,翻译本科成绩,准备各种资料,提交申请,直到拿到offer入学。

或许多年以后,我还能回忆起这段毕业N年后迟来的海外留学经历,回忆起考试前的忙碌,作业和项目deadline前的紧张。毕竟,我也为之努力过了,这个曾经的缺憾也终于被填补上了。

当然,在各种不同课程的project中,我还有幸认识到了一群同样在新加坡一边上班一边攻读master的队友们,甚至还有跟我在同一个公司的同事,也算是很新奇的体验了。

趁着毕业之际,把之前在NUS学习时候的各种照片和视频整理了一下,制作了一份毕业纪念视频:

演唱会

Master的课程完结之后,今年倒是看了很多场的演唱会。首先是4月Eason演唱会的新加坡场:

还有不老歌神张学友:

有我最喜欢的爱尔兰乐队: Kodaline

最后还有年底张信哲巡演的新加坡场:

现场的体验确实比平时听Spotify上的音乐更震撼,另外,舞台效果还是挺棒的。

旅行

年底的年假还剩了十来天,所以计划了一场跨越几个城市的“毕业旅行”,先后去了吉隆坡,香港,台北和东京。

吉隆坡篇

十一月的吉隆坡和新加坡差不多,仍然是盛夏。双子塔的夜景不错,Bukit Bintang的夜市人潮汹涌,颇有赛博朋克的风格。从新加坡飞吉隆坡是我遇到最短途的飞行了,飞机起飞不到20分钟,就开始降落了……

香港篇

十二月从新加坡飞去了香港,两天的时间匆匆打卡了小红书上各个比较有名的景点。怪兽大厦是曾经变形金刚电影的取景地,密集的房屋给人很有压抑的感觉。

中途还去打卡了香港大学,正好遇到他们的毕业季

当然,必打卡的地方还有太平山上的夜景

台北篇

年底得知入台证放开申请之后,立马提交了资料,一周左右就到手了。正好从香港飞去台湾也很近,这一段行程算是无缝衔接上了。

位于台北市附近的野柳地址公园风景很不错

十分的瀑布

九份老街

东京篇

如果年底的台北还是深秋,东京就算是彻彻底底的冬天了。从台北乘坐虎航直飞东京,其实也挺近的,正好也用上了刚申请的日本五年签。

打卡了地标建筑晴空塔

徒步上野公园

“混进” 了东京大学

东京都厅展望室的夜景

明治神宫

代代木公园

镰仓,灌篮高手打卡地

涩谷SKY观景台

六本木的圣诞点灯

御茶之水站

也许看腻了热带国家的风景,这次旅行台北和东京整体体验还挺不错,值得再去。

总结

回顾了一下去年对今年的展望:顺利毕业,回国,策划一次年底的旅行,好像都实现了。

今年终于可以腾出一点心思,重新思考一下工作以及后续的发展。希望在新的一年,能重新参与到开源项目中,并阅读一些其他感兴趣的书籍。

The Heap Sort Algorithm Explained

2023-10-10 06:40:30

The Heap

A heap is a specialized binary tree data structure that satisfies the heap property. In a heap, each node has at most two children, and the key (the value) of each node is greater than or equal to (in a max heap) or less than or equal to (in a min heap) the keys of its children. The binary tree representation of a heap is a complete binary tree, meaning that all levels of the tree are completely filled except possibly for the last level, which is filled from left to right.

Heap Sort

Heap sort is a comparison-based sorting algorithm that uses a max heap to sort elements in ascending order. The algorithm works by first building a max heap from the input array. It then repeatedly extracts the maximum element from the heap and places it at the end of the array. This process is repeated until all elements have been extracted and the array is sorted in ascending order.

Heap sort has a time complexity of O(nlogn) and is an in-place sorting algorithm, meaning it does not require any additional memory beyond the input array.

An array can be used to represent the heap structure by using the following indexing scheme:

  • The root of the heap is at index 0.
  • For any node at index i, its left child is at index 2*i+1 and its right child is at index 2*i+2.

Using this indexing scheme, we can represent a heap as an array and perform heap operations on it efficiently.

For example, to build a max heap from an array, we can start at the last non-leaf node (which is at index n/2-1, where n is the size of the array) and perform the heapify operation on each node from this index down to the root. The heapify operation ensures that the node is the largest of the three by swapping it with the largest child if necessary. By calling heapify on each node from the last non-leaf node down to the root, we ensure that the entire array represents a max heap.

An C++ Example

Here is an C++ example that builds a max heap based on the input vector:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
void heapify(std::vector<int>& vec, int n, int i) {
int largest = i;
int left = 2 * i + 1;
int right = 2 * i + 2;

if (left < n && vec[left] > vec[largest]) {
largest = left;
}

if (right < n && vec[right] > vec[largest]) {
largest = right;
}

if (largest != i) {
std::swap(vec[i], vec[largest]);
heapify(vec, n, largest);
}
}

void build_max_heap(std::vector<int>& vec) {
int n = vec.size();

for (int i = n / 2 - 1; i >= 0; i--) {
heapify(vec, n, i);
}
}

The build_max_heap function constructs a max heap from a given vector of integers. A max heap is a binary tree where the value of each node is greater than or equal to the values of its children. The build_max_heap function starts by finding the index of the last non-leaf node in the binary tree, which is n/2 - 1, where n is the size of the vector.

It then iterates from this index down to the root of the tree, calling the heapify function on each node.

The heapify function takes a node and its two children and ensures that the node is the largest of the three by swapping it with the largest child if necessary. By calling heapify on each node from the last non-leaf node down to the root, the build_max_heap function ensures that the entire binary tree is a max heap.

Now we call this function and construct a max heap:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
int main() {
std::vector<int> vec = {3, 1, 4, 1, 5, 9, 2, 6, 5, 3, 5};

// create a maximum heap
build_max_heap(vec);

// print the maximum element
std::cout << "Maximum element: " << vec.front() << std::endl;

// print the whole vector
std::cout << "The heap: ";
for (auto i : vec) {
std::cout << i << " ";
}

return 0;
}

The output is:

1
2
Maximum element: 9
The heap: 9 6 4 5 5 3 2 1 1 3 5

If we append a new number 12 to the end of the vector and re-construct the max heap:

1
2
3
4
5
6
7
vec.push_back(12);
build_max_heap(vec);

std::cout << "\nThe heap: ";
for (auto i : vec) {
std::cout << i << " ";
}

The output is:

1
The heap: 12 6 9 5 5 4 2 1 1 3 5 3

We will see that, after re-construct the max heap, the newly appended element 12 becomes to the top element of this max heap.

The Heap In C++ Standard Library

std::make_heap and std::priority_queue are both C++ standard library containers that are used to implement heaps. However, they have some differences in their functionality and usage:

  • std::make_heap is a function that takes a range of elements and rearranges them into a heap. It does not provide any additional functionality beyond constructing the heap. Once the heap is constructed, you can use other algorithms like std::sort_heap or std::pop_heap to manipulate the heap.

  • std::priority_queue is a container adapter that provides a priority queue data structure. It is implemented using a heap and provides additional functionality beyond just constructing the heap. It allows you to insert elements into the priority queue, remove the highest-priority element, and access the highest-priority element without removing it. It also provides a way to customize the comparison function used to determine the priority of elements.

The Heap Related LeetCode Problems

215. Kth Largest Element in an Array

347. Top K Frequent Elements

703. Kth Largest Element in a Stream

Modern C++ 学习笔记 -- 左值与右值

2023-09-07 06:47:29

左值(lvalues)与右值(rvalues)的概念

左值和右值是Modern C++中引入的新概念。简而言之:

  • 左值位于等号左边,我们可以对左值进行取地址操作。
  • 右值位于等号右边,本质上是一个数值,即 literal constant,我们没法对它进行取地址操作。
1
int x = 999; // x 是左值, 999是右值

我们,可以把左值大致想象为一个容器,而容器中存放的就是右值,如果没有这个容器,其中的右值将会失效。对于以下的程序,我们在编译的时候将会得到类似的错误:

1
error: lvalue required as left operand of assignment

1
2
int x;
123 = x;

很显然,等号左边需要的是一个左值,而123作为一个 literal constant 类型,是没有办法充当左值的。同样,我们也没法对一个右值进行取地址的操作:

1
2
int *x;
x = &123; //无法对右值取地址

编译器报错:

1
error: lvalue required as unary '&' operand

左值到右值的隐式转换

左值在很多情况下有可能被转换为右值,比如在C++中的 - 运算符,它将两个右值作为参数,并将计算结果作为右值返回。

1
2
3
int x = 10;
int y = 5;
int z = x - y;

在上面的程序片段中,我们明显看到x, y本身是左值,但是却以右值的身份参与了减法运算。这是如何做到的呢?答案是编译器对左值做了隐式的转换,将x和y转换成为了右值。C++中其他的乘法,除法和加法运算也是同样如此。

如果左值能被转换成右值,那么右值本身能被转换成左值吗?答案是 否定 的.

左值引用与右值引用

C++中引入引用的概念,是为了在程序中方便的通过引用修改原变量的值,并且,在调用方法传参的过程中,传递引用可以避免拷贝。在通常情况下,左值引用只能指向左值,而右值引用只能指向右值。听起来比较废话,但是也有特殊的情况。

左值引用

1
2
3
int x = 10;
int& ref_x = x;
ref_x++;

在上面的示例程序中,我们定义了一个左值x,然后赋值10。随后定义了一个引用,指向x。因此,ref_x成为x的引用,它就叫做左值引用。通过操作ref_x,我们就可以改变x的值。

如果我们把上面的程序简化为:

1
int& ref_x = 10;

在编译的时候,我们会得到类似的错误:

1
cannot bind non-const lvalue reference of type 'int&' to an rvalue of type 'int'

显然,左值引用只能指向一个左值,而不能指向一个右值。不错从错误信息中,我们方法可以得出另外一种写法:

1
const int& ref_x = 10;

根据编译器的规则,我们被允许通过定义一个const类型的左值来指向右值。不过既然这个左值被定义成了const,没有办法修改指向的值。

右值引用

C++中的右值引用以&&表示。通过右值引用,可以修改其指向的右值。

1
2
int&& ref_x = 10; //定义右值引用
ref_x--; //通过右值引用修改其指向的右值

如果我们尝试将右值引用指向一个左值:

1
2
int x = 10;
int&& ref_x = x;

编译器也会抛出类似的错误,告诉我们不能把一个右值引用指向左值。

1
error: cannot bind rvalue reference of type 'int&&' to lvalue of type 'int'

左右值引用的本质

通过一个简单的示例程序,我们就能知道左值引用和有值引用的本质。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
void increase(int&& input) {
input++;
}

int main() {
int x = 10;

int& ref_a = &x;
int&& ref_b = std::move(x);

increase(x); // 编译错误,不能传入左值
increase(ref_a); // 编译错误,不能传入 左值引用
increase(ref_b); // 编译错误,右值引用本身是一个左值

increase(std::move(a)); // 编译通过
increase(std::move(ref_a)); // 编译通过
increase(std::move(ref_b)); // 编译通过

increase(7); //编译通过,7是右值

return 0;
}

从上面的代码示例中,我们可以看出右值引用 ref_b 本身也是一个左值,在调用 increase 的时候,需要通过 std::move 转换为右值,编译器才不会报错。

通过以上的例子,我们可以总结出如下的规律:

  • 左右值引用的引入,都是为了避免拷贝。
  • 左值引用通常指向左值,通过添加 const 关键字约束也能指向右值,不过无法对右值进行修改。
  • 右值引用本质上也是一个左值,右值引用通常情况下指向右值,不过通过 std::move 等形式也可以指向左值。

右值引用与移动语义

在前面的例子中,我们已经涉及到了 std::move 这样的操作。右值引用配合 std::move 通常能实现移动语义,从而实现避免拷贝,提升程序性能。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#include <iostream>
#include <vector>

int main() {
std::vector<std::string> list;

std::string str_a = "Hello";
std::string str_b = "World";

list.push_back(str_a);
list.push_back(std::move(str_b));

std::cout << "str_a: " << str_a << std::endl;
std::cout << "str_b: " << str_b << std::endl;
std::cout << "list[0]: " << list[0] << std::endl;
std::cout << "list[1]: " << list[1] << std::endl;
return 0;
}

如果运行上面的示例程序,我们会得到这样的程序输出:

1
2
3
4
str_a: Hello
str_b:
list[0]: Hello
list[1]: World

很明显,在str_a被添加到vector的时候,并没有涉及到移动语义,所以str_a的值被拷贝到了vector中。而在把str_b添加到vector的过程中,由于用到了std::move,所以str_b的值被移动到了vector中。之后再输出vector的值的时候,可以看到其中已经包含了str_astr_b的值。但是str_b本身的值已经被偷走了。

需要注意的是,std::move本身的名字比较有迷惑性,其实它在这里的工作只是把str_b从左值转换成右值,而不会做实际上移动资源的操作。如果我们把添加str_b的代码替换成:

1
list.push_back(static_cast<std::string&&>(str_b));

会达到一样的效果。而真正的秘密在于 std::vector 提供的两种不同的重载方法:

1
2
void push_back( const T& value );
void push_back( T&& value );

第一个重载方法接受的是左值引用,当传入 str_a 的时候,由于 const 关键字的限制,它的值会被拷贝一份,并放入到vector中,而 str_a 本身的值并不受影响。而第二个重载方法接受的是一个右值引用,push_back方法会把其值放入vector中,并转移 str_b 对字符串值 World 的所有权。这样,当我们再输出它的值的时候,发现已经为空了。

完美转发(std::forward)

完美转发(Perfect Forwarding),转发的意义在于当一个方法把其参数传递给另外一个方法的时候,不光转发参数本身,还包括其属性(左值引用保持左值引用,右值引用保持右值引用)。

std::forward 实际上也是做类型的转换,不同的是 std::move 只把左值转换为右值,std::forward 能转换为左值或右值。

std::forward<T>(arg) 中,如果类型 T 是左值,参数 arg 将被转换为左值,否则 arg 将被转换为右值。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#include <iostream>
#include <utility>

void target_func(int& arg) { std::cout << "lvalue reference" << std::endl; }

void target_func(int&& arg) { std::cout << "rvalue reference" << std::endl; }

template <typename T> void forward(T&& arg) {
target_func(std::forward<T>(arg));
}

int main() {
forward(5);
int x = 5;
forward(x);
return 0;
}

在以上的示例程序中,forward 使用一个Universal Reference类型接受一个参数,并且通过 std::forward 讲参数转发给 target_func。由于此方法有两个重载,分别接受左值引用和右值引用。在我们分别传入右值 5 和左值 x 的时候,我们发现 forward 这个方法都能准确无误的把参数转发给对应的重载方法。因此,程序的输出分别是:

1
2
rvalue reference
lvalue reference

参考文章

  1. Understanding the meaning of lvalues and rvalues in C++
  2. Cpp Reference
  3. Perfect Forwarding

告别2022

2022-12-27 08:05:28

随着疫情的反复,今年又是没能回去的一年,回想起年初的时候,去摩天轮观了夜景,也去金沙酒店的无边泳池游了一回,一年的时间总是飞逝而过。


https://xiaozhou.net/pics/2022/sg1.jpg

https://xiaozhou.net/pics/2022/sg3.jpg

https://xiaozhou.net/pics/2022/sg2.jpg

工作

新加坡的“复工复产”,比起国内来得更早一些。上半年还都在家里办公,公司员工可以自由选择在家上班或者去公司,从下半年开始,就陆续都回公司上班了。再到年底,几乎全员都被要求回公司上班了。

https://xiaozhou.net/pics/2022/sg4.jpg

然而,今年的经济形式并不好,再加上美联储加息,公司各种相关的负面新闻层出不穷,股价连续暴跌。经历了年中和年末的几波裁员,身边有不少同事幸运的领到了大礼包。说是大礼包一点也不过分,都是N+2起,但是我命中仿佛一直和大礼包没有太大的缘分。当年Moto关闭成都研发中心,传闻N+7的大礼包我也没领到,在领大礼包之前竟然主动离职了,眼睁睁错过一个亿。这次也一样,一波又一波的裁员也没轮到我,除了老实搬砖也干不了啥。公司的这波裁员也上了国内的新闻,脉眿上都被刷屏了,希望明年的经济能尽快好转起来。

学习

之前因为疫情,我的master课程有两个学期都是完全在线上完成的。从今年8月开始,也就是我读master课程的第三个学期,NUS也要求所有的课程和考试都转成线下形式了。好在Shopee离校园很近,下班后可以走到街对面直接乘坐NUS的校车去上课,时间刚刚好。前两个学期都是上网课,也没有太多机会去熟悉校园,这次趁着去学校上课的机会正好把校园逛了个遍:

UTown的风景

https://xiaozhou.net/pics/2022/nus1.jpg

https://xiaozhou.net/pics/2022/nus2.jpg

UTown的大草坪

https://xiaozhou.net/pics/2022/nus3.jpg

https://xiaozhou.net/pics/2022/nus4.jpg

校园礼品店

https://xiaozhou.net/pics/2022/nus5.jpg

校园内的李光前自然历史博物馆

https://xiaozhou.net/pics/2022/nus6.jpg

参加线下课程,虽然每次得往学校里跑,不过整体体验还是不错的,那种N年前在大学上课的感觉又回来了,哥的青春也跟着回来了:

自从第三学期开始,明显感觉比之前两个学期轻松了许多,因为前两个学期都是选满了三门课程,周末的时间几乎全用来做project和肝作业了。后两个学期分别只有两门课程,workload自然也小了许多。正值年底,第三个学期也结束了,来年开学后还有最后一个学期,不出意外的话,我就可以毕业了。希望来年最后两门课能卷出个高分,顺利毕业。

开源项目

今年投入开源项目的精力并不多,开学上课过后,也没有太多精力维护开源项目。当然,其间也投入了小部分精力继续维护已有的项目,更新了几个版本。另外,也花了一点时间给GitHub配置了项目赞助功能,还意外得到了一些赞助。 除此之外,LunarVim 是我今年开始玩的Vim配置,用起来挺顺手,于是我也给这个开源项目的作者赞助了一点美刀,算是聊表心意。

今年开源项目的总赞助收入是: 9.99新币

https://xiaozhou.net/pics/2022/github.png

投资

今年的股票和期权账户只能用惨不忍睹来形容。去年底开始,公司股票开启暴跌模式,年初的时候,特斯拉的期权又亏了2W多刀。快到年底的时候,特斯拉和公司股票又开始狂跌。掐指一算,哥的身价亏了几百个W的软妹币。不过虽然亏,税还得照缴,生活也还得继续。今年的投资,就这样了吧。账户也不想看了,也许再等两三年再看看会不会好一点。

程序员划水实践

说是划水实践其实一点也不错,年底的时候约上NUS的校友(其实都是程序员),实践了一下程序员应该如何正确的划水。从巴西立公园租了几个皮艇,一路划到乌敏岛,中午在一个海上的渔家餐馆吃饭,然后再划回公园。单程4公里,来回大概8公里。

https://xiaozhou.net/pics/2022/boat1.jpg

https://xiaozhou.net/pics/2022/boat2.jpg

旅行

年底第三学期刚结束,去电影院看了Coldplay的一场演唱会,然后开始准备旅行攻略。

https://xiaozhou.net/pics/2022/sg5.jpg

之前在Youtube上有看到西澳Perth的各种旅行视频,被种了草。趁着周末的空余时间,提交了澳洲的旅行签证申请。看官网本来以为审批的过程需要3个月以上,如果真是这样的话,年底旅行得换个地方了,当时的Plan B是准备去新加坡旁边的马来西亚走一圈。没想到的是提交完材料之后一周多的时间就收到审批通过的邮件。然后,也就顺理成章的有了这次的澳洲之行。

旅行前好好的准备了一下行程,还顺便把新加坡的驾照转换成了国际驾照,按照行程安排,在网上选酒店,租车,其间也参考了不少的旅行攻略。

https://xiaozhou.net/pics/2022/trip1.jpg

这旅行的大致路线是:

  1. 从新加坡直飞Perth,把我信用卡积分转换得到的几万新航里程用来换了一张免费的单程机票,相当于是白嫖了。去Perth后直接租车,游览本地的景点,然后顺着西海岸线路一路往北,去几个著名的景点打卡。
  2. 在Perth的最后一晚,回到Perth市区,第二天一早开车去机场还车,然后从Perth飞去墨尔本拜访一下十几年没见的前同事一家,顺便在墨尔本周边逛逛。
  3. 从墨尔本直飞回新加坡,其实这段行程略长,在飞机上8小时,跨越了3个时区。

行程计划:

https://xiaozhou.net/pics/2022/trip.png

万事已俱备,在十二月初的时候,跟公司请了一周的年假,换了一些澳币现金,开启了这次的澳洲之旅。掐指一算,这也是我在疫情快三年之后,第一次乘飞机出去的长途旅行。

https://xiaozhou.net/pics/2022/trip2.jpg

https://xiaozhou.net/pics/2022/trip3.jpg

https://xiaozhou.net/pics/2022/trip4.jpg

新加坡是一年四季都是夏天,Perth正好在南半球,也是夏季。所以出行的衣服可以无缝衔接,都是夏装就行了。长袖长裤也需要备两件,因为跟新加坡不一样的是,澳洲白天和晚上温差更大一些,晚上的风能让你感觉一秒入秋。从新加坡飞Perth只需要大概5小时左右的时间,更神奇的是,西澳的Perth和新加坡,以及和国内都一样,都是东八区,无时差。

https://xiaozhou.net/pics/2022/trip5.jpg

这次租的车是个日产的SUV,在国内的同款叫东风日产奇骏。澳洲的车跟新加坡的也一样,驾驶台在右侧,车辆靠左行驶。从西澳Perth沿着海岸线一路北上了几百公里,感觉风景绝佳,一旁是公路,另一侧就是大海。随手一拍也能出不错的照片。

https://xiaozhou.net/pics/2022/trip13.jpg

Geraldton的海滩

https://xiaozhou.net/pics/2022/trip6.jpg

HMAS Sydney II Memorial

https://xiaozhou.net/pics/2022/trip7.jpg

https://xiaozhou.net/pics/2022/trip12.jpg

The Pinnacles Desert

https://xiaozhou.net/pics/2022/trip8.jpg

Cairn Beach

https://xiaozhou.net/pics/2022/trip9.jpg

Cervantes

https://xiaozhou.net/pics/2022/trip10.jpg

Phillip Island

https://xiaozhou.net/pics/2022/trip11.jpg

实事证明,4天的时间游Perth还是不够的,粉红湖泊因为季节不对,关闭了,Perth的野生动物园也没时间去,Rottnest Island值得花上一整天的时间去,可惜也因为时间关系没包含在此次行程中。这次只是一路北上自驾,从Perth一直往南又是另外一条不错的线路,看来下次有机会还得再去深度体验一下。

这次用Dji的Action 3和手机拍了很多4K的视频,所有的素材整理出来有接近15GB。回新加坡之后,我把之前买的Final Cut Pro翻了出来,第一次学视频剪辑,剪出了一个3分多钟的vlog,供同学们欣赏:

来年期望

  • Master课程顺利毕业
  • 回国一次
  • 策划一次去其他国家的旅行