C++ Primer
- Task1 Copy-On-Write Trie
- Task2 Concurrent Key-Value Store
- Task3 Debugging
- Task4 SQL String Functions
CMU-15445汇总
本文对应的project版本为CMU-Spring-2023的project0
默认读者已经学会了字典树Trie
Task1 Copy-On-Write Trie
Task1要求实现一个可持久化字典树,可持久化字典树的定义大概就是,每次更改操作(Put或者Remove)都会生成一个新的字典树版本,并保留历史版本,且各版本间的查询操作不相互影响。
按照文档要求,我们主要实现Trie
类中的三个方法:
const T* Get(std::string_view key) const
Trie Put(std::string_view key, T value) const
Trie Remove(std::string_view key) const
对于Get
方法,比较简单,就是普通的字典树查询而已,顺着树往下走到节点处,取出值即可。
对于Put
方法,相对复杂一点。假设key
的长度为n
,那么需要新建出n + 1
个结点(包括一个新根),我的具体做法是从原始的根开始,在旧的树上沿着key
的每个字符往下走,走到某个结点时,复制这个结点(包括该结点的children_
指针),并修改上一个复制结点的children_
(如果有上一个的话,即当前复制结点不为根)。举个例子,假设当前的字符为c
,上一个复制出来的结点为A
,当前复制出来的结点为B
,则需要把结点A
的children_[c]
指向B
。
最后,对于Remove
方法,和Put
有点像,在树上走的过程中也要复制路径上的所有结点,不同点在于最后走到的点要从TrieNodeWithValue
变为TrieNode
,且如果一个结点不为TrieNodeWithValue
并且没有任何孩子,则需要删除。
其实思路上不是特别复杂,但是在打代码时,会一直编译报错,通过这个Task对智能指针更加的熟悉了。不过还对C++的几个类型转换函数不是很懂(比如dynamic_cast
),后续研究下并输出博客。
Task2 Concurrent Key-Value Store
这个Task相对比较简单,就是利用上一个Task的可持久化Trie
加上互斥锁实现一个支持并发读写Trie
,也是实现上述几个方法。
对于Get
方法,比较简单,获取root_lock_
之后拿到可持久化Trie
(其实就是拿到某个版本的根节点),调用该版本的Get
方法即可。
对于Put
方法,要先获取write_lock_
保证只有一个写者,然后调用可持久化Trie
的Put
方法即可,再获取root_lock_
并将当前指向的根节点更新。
Remove
方法与Put
一致,不再赘述。
Task3 Debugging
这个Task相对也比较简单,就是运行下程序输出结果而已,但是随机数生成在我本地wsl与在gradescope上生成的不同,导致无法通过gradescope测试,于是寻找解决办法,最后发现discord上有助教发的一段话
替换一下代码重新看结果即可
auto trie = Trie();
trie = trie.Put<uint32_t>("65", 25);
trie = trie.Put<uint32_t>("61", 65);
trie = trie.Put<uint32_t>("82", 84);
trie = trie.Put<uint32_t>("2", 42);
trie = trie.Put<uint32_t>("16", 67);
trie = trie.Put<uint32_t>("94", 53);
trie = trie.Put<uint32_t>("20", 35);
trie = trie.Put<uint32_t>("3", 57);
trie = trie.Put<uint32_t>("93", 30);
trie = trie.Put<uint32_t>("75", 29);
Task4 SQL String Functions
这个Task最简单吧,添加一个大小写转换功能而已,主要修改的文件为(文档里好像没有写清楚):
src/include/execution/expressions/string_expression.h
src/planner/plan_func_call.cpp
功能很简单实现也很简单就不多说啦!最后附上gradescope通过截图!