Loading

2023Spring project0

Task1: copy-on-write trie

第一个task实现一个写时复制Trie树,个人理解,这个概念类似于OI中的可持久化Trie树
首先大体框架已经给出来了,主要实现三个功能,分别是Get,Put和Remove。

Get

给定一个key,返回key所对应的value。
有以下三种情况:

  1. 对应的key在Trie树中不存在,那么应该提前退出,返回nullptr。
  2. 对应的key在Trie树中存在,但是其并非一个带值的节点,返回nullptr。
  3. 对应的key在Trie树中存在,且其是一个带值的节点,那么将该节点的值返回。

我的思路是采用迭代而非递归,用一个node指针指向当前所在的节点,不断的遍历node对应的children,如果中间发现找不到对应的children,则是case1,直接返回nullptr。

迭代结束后,node会到达对应的key所指向的节点,利用dynamic_cast尝试将其转换为带值节点,如果转换失败就为case2返回nullptr。否则转换成功,case3,返回这个节点的值即可。

Put

给定一个key和value,在Trie树中存储。由于copy-on-write的Trie树的特性,这个key对应路径上的所有节点,都应该新建,其余的所有节点就复用以前版本的Trie树中节点。

所有可以实现一个辅助函数PutHelper,该函数主要就是DFS用。

  1. Trie树中无任何节点。那么PutHelper肯定是不需要复用以前的节点了,一路沿着key创建新节点即可。
  2. Trie树中存在一些节点。首先需要copy先前Trie树的root节点。
    image
    如图所示,由于父结点和子结点之间是通过std::map来存储映射的,所以可以直接插入新节点,新节点会覆盖掉旧节点的映射。
    这里需要注意的地方是,new_node同样需要根据先前的节点拷贝过来,这是为了不丢失下一层节点的连接关系。
    image

Remove

给定一个Key,在Trie树中删除掉这个key对应的value。
同样在删除操作中,对应的key路径上的节点我们需要新建,其他的需要复用。

  1. 对应的key在Trie树中不存在,那么此时我们不需要做任何操作,将原先的Trie树原封不动的返回出去即可。
  2. 对应的key在Trie树中存在路径,但是终端节点并不是一个带值节点。那么说明这个节点时其他节点的路径,不能删去,此时我们不需要做任何操作,同样直接返回即可。
  3. 对应的key在Trie树中存在路径,且终端节点是一个带值节点。此时需要分两种情况讨论。
    1. 该终端节点是一个叶子节点。那么我们可以直接将该叶子节点删去,同时在父结点的children列表中删去该节点。
    2. 该终端节点不是一个叶子节点,那么我们应该将该节点从带值节点转换成一个不带值节点,而链接关系之类的应该保持原封不动。

Task2:Concurrent Key-Value Store

其实就是实现一个多线程环境下可用的Trie。本质上是在task0的trie树基础上,给他封装了一下。
同样需要实现三个功能,分别是Get、Put和Delete。与task0中的Trie不同的是,Put和Delete并没有返回值,也就是说需要在原数据结构上直接进行修改。

Get

首先需要获取Trie树的树根,这一步需要树根锁,获取完树根后解锁。
然后直接调用Trie::Get即可,由于不改变数据结构,不需要上锁,然后返回值即可。

Put

首先获取写锁,然后再获取树根锁,从而得到树根,释放树根锁(让其他读者可以获取树根进行读入)。然后调用Trie::Put,完成后需要更新树根,因此又需要获取树根锁,更新完再释放。最后释放写锁。(保证永远最多只有一个写者)

Delete

与Put是同理的,只不过换成调用Trie::Delete。

Task3:Debugging

在trie_debug_test.cpp里可以看到三个断言,只需要找到能让这三个断言通过的值就可以。
因为我不会用gdb,所以这里直接用cout打印来调试。
最后把答案写到trie_answer.h即可。

这里有一个坑,即本地的测试数据和gradescope平台上的测试数据不一样。需要把TrieDebugger改成以下数据

 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

实现Lower和Upper函数,这两函数很简单,怎么实现就不说了。这个的主要难点在于在BusTub中注册函数。

找到plan_func_call.cpp这个文件,实现里面的函数Planner::GetFuncCallFromFactory即可。

image
最终也是顺利通过测试,拿到满分。

posted @ 2023-07-30 15:37  烤肉kr  阅读(58)  评论(0编辑  收藏  举报