学习 Rust cookbook 之算法篇(algorithm)part 2

邵弘致
2023-12-01

学习 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 的从小到大排序。

 类似资料: