diff --git a/book.toml b/book.toml index 20e7368a..932623b4 100644 --- a/book.toml +++ b/book.toml @@ -1,7 +1,7 @@ [book] authors = ["sunface"] language = "zh-CN" -title = "Rust语言圣经(Rust教程 Rust Course)" +title = "Rust语言圣经(Rust Course)" src = "src" [output.html] diff --git a/src/too-many-lists/advanced-lists/double-singly.md b/src/too-many-lists/advanced-lists/double-singly.md index a9628703..45638ccf 100644 --- a/src/too-many-lists/advanced-lists/double-singly.md +++ b/src/too-many-lists/advanced-lists/double-singly.md @@ -20,3 +20,201 @@ struct List { } ``` +这里将之前的 `List` 引入进来,并重命名为 `Stack`,接着,创建一个新的链表。现在既可以向左增长又可以向右增长。 + +```rust +pub struct Stack { + head: Link, +} + +type Link = Option>>; + +struct Node { + elem: T, + next: Link, +} + +impl Stack { + pub fn new() -> Self { + Stack { head: None } + } + + pub fn push(&mut self, elem: T) { + let new_node = Box::new(Node { + elem: elem, + next: self.head.take(), + }); + + self.head = Some(new_node); + } + + pub fn pop(&mut self) -> Option { + self.head.take().map(|node| { + let node = *node; + self.head = node.next; + node.elem + }) + } + + pub fn peek(&self) -> Option<&T> { + self.head.as_ref().map(|node| { + &node.elem + }) + } + + pub fn peek_mut(&mut self) -> Option<&mut T> { + self.head.as_mut().map(|node| { + &mut node.elem + }) + } +} + +impl Drop for Stack { + fn drop(&mut self) { + let mut cur_link = self.head.take(); + while let Some(mut boxed_node) = cur_link { + cur_link = boxed_node.next.take(); + } + } +} +``` + +稍微修改下 `push` 和 `pop`: +```rust +pub fn push(&mut self, elem: T) { + let new_node = Box::new(Node { + elem: elem, + next: None, + }); + + self.push_node(new_node); +} + +fn push_node(&mut self, mut node: Box>) { + node.next = self.head.take(); + self.head = Some(node); +} + +pub fn pop(&mut self) -> Option { + self.pop_node().map(|node| { + node.elem + }) +} + +fn pop_node(&mut self) -> Option>> { + self.head.take().map(|mut node| { + self.head = node.next.take(); + node + }) +} +``` + +现在可以开始构造新的链表: +```rust +pub struct List { + left: Stack, + right: Stack, +} + +impl List { + fn new() -> Self { + List { left: Stack::new(), right: Stack::new() } + } +} +``` + +当然,还有一大堆左左右右类型的操作: +```rust +pub fn push_left(&mut self, elem: T) { self.left.push(elem) } +pub fn push_right(&mut self, elem: T) { self.right.push(elem) } +pub fn pop_left(&mut self) -> Option { self.left.pop() } +pub fn pop_right(&mut self) -> Option { self.right.pop() } +pub fn peek_left(&self) -> Option<&T> { self.left.peek() } +pub fn peek_right(&self) -> Option<&T> { self.right.peek() } +pub fn peek_left_mut(&mut self) -> Option<&mut T> { self.left.peek_mut() } +pub fn peek_right_mut(&mut self) -> Option<&mut T> { self.right.peek_mut() } +``` + +其中最有趣的是:还可以来回闲逛了。 +```rust +pub fn go_left(&mut self) -> bool { + self.left.pop_node().map(|node| { + self.right.push_node(node); + }).is_some() +} + +pub fn go_right(&mut self) -> bool { + self.right.pop_node().map(|node| { + self.left.push_node(node); + }).is_some() +} +``` + +这里返回 `bool` 是为了告诉调用者我们是否成功的移动。最后,再来测试下: +```rust +#[cfg(test)] +mod test { + use super::List; + + #[test] + fn walk_aboot() { + let mut list = List::new(); // [_] + + list.push_left(0); // [0,_] + list.push_right(1); // [0, _, 1] + assert_eq!(list.peek_left(), Some(&0)); + assert_eq!(list.peek_right(), Some(&1)); + + list.push_left(2); // [0, 2, _, 1] + list.push_left(3); // [0, 2, 3, _, 1] + list.push_right(4); // [0, 2, 3, _, 4, 1] + + while list.go_left() {} // [_, 0, 2, 3, 4, 1] + + assert_eq!(list.pop_left(), None); + assert_eq!(list.pop_right(), Some(0)); // [_, 2, 3, 4, 1] + assert_eq!(list.pop_right(), Some(2)); // [_, 3, 4, 1] + + list.push_left(5); // [5, _, 3, 4, 1] + assert_eq!(list.pop_right(), Some(3)); // [5, _, 4, 1] + assert_eq!(list.pop_left(), Some(5)); // [_, 4, 1] + assert_eq!(list.pop_right(), Some(4)); // [_, 1] + assert_eq!(list.pop_right(), Some(1)); // [_] + + assert_eq!(list.pop_right(), None); + assert_eq!(list.pop_left(), None); + + } +} +``` + +```shell +> cargo test + + Running target/debug/lists-5c71138492ad4b4a + +running 16 tests +test fifth::test::into_iter ... ok +test fifth::test::basics ... ok +test fifth::test::iter ... ok +test fifth::test::iter_mut ... ok +test fourth::test::into_iter ... ok +test fourth::test::basics ... ok +test fourth::test::peek ... ok +test first::test::basics ... ok +test second::test::into_iter ... ok +test second::test::basics ... ok +test second::test::iter ... ok +test second::test::iter_mut ... ok +test third::test::basics ... ok +test third::test::iter ... ok +test second::test::peek ... ok +test silly1::test::walk_aboot ... ok + +test result: ok. 16 passed; 0 failed; 0 ignored; 0 measured +``` + +上上下下,左左右右,BABA,哦耶,这个链表无敌了! + +以上是一个非常典型的手指型数据结构finger data structure,在其中维护一个手指,然后操作所需的时间与手指的距离成正比。 + diff --git a/内容变更记录.md b/内容变更记录.md index 9549b1d5..8dc78adf 100644 --- a/内容变更记录.md +++ b/内容变更记录.md @@ -1,6 +1,11 @@ # ChangeLog 记录一些值得注意的变更。 +## 2022-03-28 + +- 新增章节:[双单向链表](https://course.rs/too-many-lists/advanced-lists/double-singly.html) +- 优化样式:增加目录中的区域性标题、修改 github 图标和说明,通过 js 增加访问者统计 + ## 2022-03-27 - 新增章节: [不错的unsafe队列 - 额外的操作](https://course.rs/too-many-lists/unsafe-queue/extra-junk.html)