学习 Rust cookbook 之算法篇(algorithm)part 2
上篇,我们了解了,可以用 rand crate 的接口进行生成随机数的操作,其中有生成指定类型的随机值,生成指定范围的随机值。这一篇,我们将探讨以下,从一堆元素中,随机获取其中一个的场景。
场景
获取自定义类型的随机值
通常的做法是:假如有一个用户自定义的类型,针对其中的所有属性,挨个赋值随机值,这样得到的类型实例就相当于随机的了。但在 Rust 的 rand crate 中有一个独特的做法。
按照 cookbook 中的介绍,已自定义类型 Point 为例,为 Standard
实现 Distribution<Point>
trait。这里的 Distribution<Point>
在 Rust 中被称为“具象化”类型。嗯,名字听起来比较陌生,Java 中也有这个概念。不管这个概念,先实现一波:
#[derive(Debug)]
struct Point {
x: i32,
y: i32,
}
impl Distribution<Point> for Standard {
fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> Point {
let (rand_x, rand_y) = rng.gen();
Point{
x: rand_x,
y: rand_y,
}
}
}
pub fn test_point() {
let mut rng = rand::thread_rng();
let rand_point: Point = rng.gen();
println!("random point is: {:#?}", rand_point);
}
从一堆随机的ASCII字符串中获取随机密码
嗯,这也是一个比较常用的场景,比如验证码。。好吧,更多的我也暂时想不起来了。具体实现如下:
/// 从字符串中获取随机密码
pub fn get_random_string(length: u8) ->String {
let mut rng = rand::thread_rng();
let rand_str: String = rng.sample_iter(&Alphanumeric)
.take(length as usize)
.collect();
return rand_str;
}
从给定的字符串中获取随机字符串
这个和上方的实现类似,不同的是使用的是用户给定的字符串
/// 从给定字符串中获取随机字符串
pub fn get_random_string_from_customer_str(length: u8) -> String {
let mut rng = rand::thread_rng();
let customer_str: &[u8] = b"ABCDEFGHIJKLMNOPQRSTUVWXYZ\
abcdefghijklmnopqrstuvwxyz\
0123456789)(*&^%$#@!~";
let rand_str: String = (0..length)
.map(|_| {
let idx = rng.gen_range(0, customer_str.len());
customer_str[idx] as char
})
.collect();
rand_str
}
数组
整数的排序
Rust 中的数组类型是 std::vec::Vec
。 通过宏 vec![]
可声明一个数组:
let arr1 = vec![21,32,121, 90, 98, 67, 43];
紧接着可以调用数组自带的排序方法: sort()
:
arr1.sort();
此时可以打印出排序后的数组:println!("the num is:{:?}", arr1);
the num is:[21, 32, 43, 67, 90, 98, 121]
浮点数的排序
浮点数可以用上方的 sort
进行排序吗?大难是不能。不行你试试。
试试就试试:
let mut f1 = vec![21.0, 9.8, 21.5, 9.08, 72.1, 26.9];
f1.sort();
println!("{:?}", f1);
编译时,编译器会提示:
the trait
std::cmp::Ord
is not implemented for{float}
在 cookbook 中,推荐的浮点数排序方式是使用 sort_by
方法。示例如下:
let mut f1 = vec![21.0, 9.8, 21.5, 9.08, 72.1, 26.9];
f1.sort_by(|a, b| {
a.partial_cmp(b).unwrap()
});
println!("{:?}", f1);
结构体数组的排序
尝试了整数的排序、浮点数的排序,接下来,探索一下用户自定义类型数据的排序。一个结构体排序,一定是有依据的字段,比如数据库中,查询多条数据,拉取数据数据时,例如使用 order by id desc
,其中的 id
就是排序的依据。那么我想,在 Rust 的结构体中,也应该是如此。cookbook 中的描述如下:
对 Person 结构的 Vector 进行排序,通过属性name和age的自然顺序(按名称和年龄)。为了使 Person 可排序,你需要四个 traitEq,PartialEq,Ord和PartialOrd。可以简单地derive出这些特征。您还可以使用一个vec:sort_by方法,提供自定义比较函数:只按年龄排序。 ——《Rust cookbook》
也就是说,我们有不止一种的方式对结构体数组排序,先来试试第一种 —— 对成员 derive 4 个 trait。我们的场景是学生考试分数相关。声明以下结构体:
#[derive(Debug, Eq, Ord, PartialEq, PartialOrd)]
struct StuResult {
name: String,
age: u8,
score: u16,
}
分别实例化几个具体值,放入数组中,并调用 sort()
对数组排序:
/// struct sort
pub fn sort_for_struct() {
let stu1 = StuResult {
name: "Wali".to_string(),
score: 0,
age: 22,
};
let stu3 = StuResult {
name: "Villian".to_string(),
score: 0,
age: 21,
};
let stu2 = StuResult {
name: "Unita".to_string(),
score: 0,
age: 23,
};
let mut stus: Vec<StuResult> = vec!(stu1, stu2, stu3);
println!("{:#?}", stus);
stus.sort();
println!("{:#?}", stus);
}
按照上方 cookbook 文中的描述,默认情况下,使用 stus.sort();
是对结构体内部字段值进行自然排序,排序结果如下:
[
StuResult {
name: "Unita",
score: 0,
age: 23,
},
StuResult {
name: "Villian",
score: 0,
age: 21,
},
StuResult {
name: "Wali",
score: 0,
age: 22,
},
]
如果你期望使用 age
进行排序,那么可以使用数组自带的 sort_by
方法:
stus.sort_by(|a, b| a.age.cmp(&b.age));
需要注意的是 sort_by 的参数是一个闭包 |a, b| a.age.cmp(&b.age)
, 该闭包的返回值类型是 Ordering
。对 age 字段排序,运行结果如下:
[
StuResult {
name: "Villian",
score: 0,
age: 21,
},
StuResult {
name: "Wali",
score: 0,
age: 22,
},
StuResult {
name: "Unita",
score: 0,
age: 23,
},
]
结果也如我们所期望,按照 age 的从小到大排序。