diff --git a/src/basic/collections/vector.md b/src/basic/collections/vector.md index 9d7ae379..e337650c 100644 --- a/src/basic/collections/vector.md +++ b/src/basic/collections/vector.md @@ -237,6 +237,159 @@ fn main() { 最后,如果你想要了解 `Vector` 更多的用法,请参见本书的标准库解析章节:[`Vector`常用方法](https://course.rs/std/vector.html) +## Vector 的排序 + +在 rust 里,实现了两种排序算法,分别为稳定的排序 `sort` 和 `sort_by`,以及非稳定排序 `sort_unstable` 和 `sort_unstable_by`。 + +当然,这个所谓的 `非稳定` 并不是指排序算法本身不稳定,而是指在排序过程中对相等元素的处理方式。在 `稳定` 排序算法里,对相等的元素,不会对其进行重新排序。而在 `不稳定` 的算法里则不保证这点。 + +总体而言,`非稳定` 排序的算法的速度会由于 `稳定` 排序算法,同时,`稳定` 排序还会额外分配原数组一半的空间。 + +### 整数数组的排序 + +以下是对整数列进行排序的例子。 + +```rust +fn main() { + let mut vec = vec![1, 5, 10, 2, 15]; + vec.sort_unstable(); + assert_eq!(vec, vec![1, 2, 5, 10, 15]); +} +``` + +### 浮点数数组的排序 + +我们尝试使用上面的方法来对浮点数进行排序: + +```rust +fn main() { + let mut vec = vec![1.0, 5.6, 10.3, 2.0, 15f32]; + vec.sort_unstable(); + assert_eq!(vec, vec![1.0, 2.0, 5.6, 10.3, 15f32]); +} +``` + +结果,居然报错了, + +``` +error[E0277]: the trait bound `f32: Ord` is not satisfied + --> src/main.rs:29:13 + | +29 | vec.sort_unstable(); + | ^^^^^^^^^^^^^ the trait `Ord` is not implemented for `f32` + | + = help: the following other types implement trait `Ord`: + i128 + i16 + i32 + i64 + i8 + isize + u128 + u16 + and 4 others +note: required by a bound in `core::slice::::sort_unstable` + --> /home/keijack/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/core/src/slice/mod.rs:2635:12 + | +2635 | T: Ord, + | ^^^ required by this bound in `core::slice::::sort_unstable` + +For more information about this error, try `rustc --explain E0277`. +``` + +原来,在浮点数当中,存在一个 `NAN` 的值,这个值无法与其他的浮点数进行对比,因此,浮点数类型并没有实现全数值可比较 `Ord` 的特性,而是实现了部分可比较的特性 `PartialOrd`。 + +如此,如果我们确定在我们的浮点数数组当中,不包含 `NAN` 值,那么我们可以使用 `partial_cmp` 来作为大小判断的依据。 + +```rust +fn main() { + let mut vec = vec![1.0, 5.6, 10.3, 2.0, 15f32]; + vec.sort_unstable_by(|a, b| a.partial_cmp(b).unwrap()); + assert_eq!(vec, vec![1.0, 2.0, 5.6, 10.3, 15f32]); +} +``` + +OK,现在可以正确执行了。 + +### 对结构体数组进行排序 + +有了上述浮点数排序的经验,我们推而广之,那么对结构是否也可以使用这种自定义对比函数的方式来进行呢?马上来试一下: + +```rust +#[derive(Debug)] +struct Person { + name: String, + age: u32, +} + +impl Person { + fn new(name: String, age: u32) -> Person { + Person { name, age } + } +} + +fn main() { + let mut people = vec![ + Person::new("Zoe".to_string(), 25), + Person::new("Al".to_string(), 60), + Person::new("John".to_string(), 1), + ]; + // 定义一个按照年龄倒序排序的对比函数 + people.sort_unstable_by(|a, b| b.age.cmp(&a.age)); + + println!("{:?}", people); +} +``` + +执行后输出: + +``` +[Person { name: "Al", age: 60 }, Person { name: "Zoe", age: 25 }, Person { name: "John", age: 1 }] +``` + +结果正确。 + +从上面我们学习过程当中,排序需要我们实现 `Ord` 特性,那么如果我们把我们的结构体实现了该特性,是否就不需要我们自定义对比函数了呢? + +是,但不完全是,实现 `Ord` 需要我们实现 `Ord`、`Eq`、`PartialEq`、`PartialOrd` 这些属性。好消息是,你可以 `derive` 这些属性: + +```rust +#[derive(Debug, Ord, Eq, PartialEq, PartialOrd)] +struct Person { + name: String, + age: u32, +} + +impl Person { + fn new(name: String, age: u32) -> Person { + Person { name, age } + } +} + +fn main() { + let mut people = vec![ + Person::new("Zoe".to_string(), 25), + Person::new("Al".to_string(), 60), + Person::new("Al".to_string(), 30), + Person::new("John".to_string(), 1), + Person::new("John".to_string(), 25), + ]; + + people.sort_unstable(); + + println!("{:?}", people); +} +``` + +执行输出 + +``` +[Person { age: 1, name: "John" }, Person { age: 25, name: "John" }, Person { age: 25, name: "Zoe" }, Person { age: 30, name: "Al" }, Person { age: 60, name: "Al" }] +``` + +需要 `derive` `Ord` 相关特性,需要确保你的结构体中所有的属性均实现了 `Ord` 相关特性,否则则会发生编译错误。`derive` 的默认实现会依据属性的顺序依次进行比较,如上述例子中,当 `Person` 的 `name` 值相同,则会使用 `age` 进行比较。 + + ## 课后练习 > [Rust By Practice](https://zh.practice.rs/collections/vector.html),支持代码在线编辑和运行,并提供详细的[习题解答](https://github.com/sunface/rust-by-practice/blob/master/solutions/collections/Vector.md)。