当前位置: 首页 > news >正文

Rust 练习册 53:位运算与过敏测试架构

Rust 练习册 53:位运算与过敏测试架构

在现实生活中,许多人对某些食物或物质过敏。医生通常使用测试来确定患者对哪些物质过敏。在 Exercism 的 “allergies” 练习中,我们将构建一个过敏测试系统,它使用一个简单的数值来表示患者的过敏情况。这不仅能帮助我们理解位运算的实际应用,还能深入学习 Rust 中的结构体实现和枚举使用。

问题背景

在这个练习中,过敏测试的结果是一个数值,其中每个位代表一种特定的过敏原。这种使用位掩码表示多个布尔值的技术在计算机科学中非常常见,比如 Unix 文件权限、网络协议标志等。

让我们先看看练习提供的结构和枚举:

pub struct Allergies;#[derive(Debug, PartialEq)]
pub enum Allergen {
Eggs,
Peanuts,
Shellfish,
Strawberries,
Tomatoes,
Chocolate,
Pollen,
Cats,
}
impl Allergies {
pub fn new(score: u32) -> Self {
unimplemented!(
"Given the '{}' score, construct a new Allergies struct.",
score
);
}
pub fn is_allergic_to(&self, allergen: &Allergen) -> bool {
unimplemented!(
"Determine if the patient is allergic to the '{:?}' allergen.",
allergen
);
}
pub fn allergies(&self) -> Vec<Allergen> {unimplemented!("Return the list of allergens contained within the score with which the Allergies struct was made.");}}

我们需要实现这个结构体,使其能够:

  1. 根据给定的分数构建 Allergies 实例
  2. 检查患者是否对特定过敏原过敏
  3. 返回患者过敏的所有过敏原列表

位运算基础

在开始实现之前,让我们理解一下位运算的原理。每种过敏原都被分配了一个特定的位位置:

#[derive(Debug, PartialEq, Clone, Copy)]
pub enum Allergen {
Eggs = 1,        // 2^0 = 1
Peanuts = 2,     // 2^1 = 2
Shellfish = 4,   // 2^2 = 4
Strawberries = 8, // 2^3 = 8
Tomatoes = 16,   // 2^4 = 16
Chocolate = 32,  // 2^5 = 32
Pollen = 64,     // 2^6 = 64
Cats = 128,      // 2^7 = 128
}

如果一个人对鸡蛋和花生过敏,他们的过敏分数将是 1 + 2 = 3。如果对鸡蛋、贝类和草莓过敏,分数将是 1 + 4 + 8 = 13。

实现 Allergies 结构体

让我们实现 Allergies 结构体:

pub struct Allergies {
score: u32,
}#[derive(Debug, PartialEq, Clone, Copy)]
pub enum Allergen {
Eggs = 1,
Peanuts = 2,
Shellfish = 4,
Strawberries = 8,
Tomatoes = 16,
Chocolate = 32,
Pollen = 64,
Cats = 128,
}
impl Allergen {
fn all() -> Vec<Allergen> {vec![Allergen::Eggs,Allergen::Peanuts,Allergen::Shellfish,Allergen::Strawberries,Allergen::Tomatoes,Allergen::Chocolate,Allergen::Pollen,Allergen::Cats,]}}impl Allergies {pub fn new(score: u32) -> Self {Allergies { score }}pub fn is_allergic_to(&self, allergen: &Allergen) -> bool {self.score & (*allergen as u32) != 0}pub fn allergies(&self) -> Vec<Allergen> {Allergen::all().into_iter().filter(|allergen| self.is_allergic_to(allergen)).collect()}}

代码解析

1. 结构体定义

pub struct Allergies {
score: u32,
}

我们在结构体中存储过敏分数,这是所有后续操作的基础。

2. 枚举定义

#[derive(Debug, PartialEq, Clone, Copy)]
pub enum Allergen {
Eggs = 1,
Peanuts = 2,
Shellfish = 4,
Strawberries = 8,
Tomatoes = 16,
Chocolate = 32,
Pollen = 64,
Cats = 128,
}

每个过敏原都有一个显式的值,这些值是 2 的幂,这样每个过敏原都对应二进制表示中的一个位。

3. 检查过敏

pub fn is_allergic_to(&self, allergen: &Allergen) -> bool {
self.score & (*allergen as u32) != 0
}

这是核心逻辑。我们使用按位与运算符 & 来检查特定的位是否被设置。例如,如果分数是 3(二进制 11),我们要检查是否对鸡蛋过敏(值为 1,二进制 01):

  11 (3)
& 01 (1)
----01 (1) != 0  // 对鸡蛋过敏

4. 获取所有过敏原

pub fn allergies(&self) -> Vec<Allergen> {Allergen::all().into_iter().filter(|allergen| self.is_allergic_to(allergen)).collect()}

这个方法遍历所有可能的过敏原,过滤出患者确实过敏的那些,并收集到一个向量中。

测试用例分析

通过查看测试用例,我们可以更好地理解需求:

#[test]
fn is_not_allergic_to_anything() {
let allergies = Allergies::new(0);
assert!(!allergies.is_allergic_to(&Allergen::Peanuts));
assert!(!allergies.is_allergic_to(&Allergen::Cats));
assert!(!allergies.is_allergic_to(&Allergen::Strawberries));
}

分数为 0 表示对任何东西都不过敏。

#[test]
fn is_allergic_to_egg_shellfish_and_strawberries() {
let allergies = Allergies::new(5);
assert!(allergies.is_allergic_to(&Allergen::Eggs));
assert!(allergies.is_allergic_to(&Allergen::Shellfish));
assert!(!allergies.is_allergic_to(&Allergen::Strawberries));
}

分数 5 是 1 + 4,表示对鸡蛋和贝类过敏,但对草莓不过敏。

#[test]
fn allergic_to_everything() {
let expected = &[
Allergen::Eggs,
Allergen::Peanuts,
Allergen::Shellfish,
Allergen::Strawberries,
Allergen::Tomatoes,
Allergen::Chocolate,
Allergen::Pollen,
Allergen::Cats,
];
let allergies = Allergies::new(255).allergies();
compare_allergy_vectors(expected, &allergies);
}

分数 255(二进制 11111111)表示对所有 8 种过敏原都过敏。

#[test]
fn scores_over_255_do_not_trigger_false_positives() {
let expected = &[
Allergen::Eggs,
Allergen::Shellfish,
Allergen::Strawberries,
Allergen::Tomatoes,
Allergen::Chocolate,
Allergen::Pollen,
Allergen::Cats,
];
let allergies = Allergies::new(509).allergies();
compare_allergy_vectors(expected, &allergies);
}

这个测试表明,超过 255 的分数不应该影响低 8 位的解释。

优化版本

考虑到分数可能超过 255,我们可以提供一个更健壮的版本:

pub struct Allergies {
score: u32,
}#[derive(Debug, PartialEq, Clone, Copy)]
pub enum Allergen {
Eggs = 1,
Peanuts = 2,
Shellfish = 4,
Strawberries = 8,
Tomatoes = 16,
Chocolate = 32,
Pollen = 64,
Cats = 128,
}
impl Allergen {
fn all() -> Vec<Allergen> {vec![Allergen::Eggs,Allergen::Peanuts,Allergen::Shellfish,Allergen::Strawberries,Allergen::Tomatoes,Allergen::Chocolate,Allergen::Pollen,Allergen::Cats,]}}impl Allergies {pub fn new(score: u32) -> Self {// 只考虑最低的8位Allergies { score: score & 255 }}pub fn is_allergic_to(&self, allergen: &Allergen) -> bool {self.score & (*allergen as u32) != 0}pub fn allergies(&self) -> Vec<Allergen> {Allergen::all().into_iter().filter(|allergen| self.is_allergic_to(allergen)).collect()}}

位运算的实际应用

位运算在实际开发中有很多应用:

  1. 权限系统:Unix 文件权限使用类似的机制
  2. 标志位:网络协议和配置选项经常使用位标志
  3. 状态管理:游戏开发中常用位运算管理对象状态
  4. 优化:位运算通常比算术运算更快

性能考虑

在实现中,我们需要注意:

  1. 时间复杂度:[is_allergic_to](file:///Users/zacksleo/projects/github/zacksleo/exercism-rust/exercises/practice/allergies/src/lib.rs#L18-L21) 是 O(1),[allergies](file:///Users/zacksleo/projects/github/zacksleo/exercism-rust/exercises/practice/allergies/src/lib.rs#L23-L25) 是 O(n)
  2. 内存效率:使用位运算可以极大地节省存储空间
  3. 缓存友好:预先计算所有过敏原列表可以提高性能

总结

通过 allergies 练习,我们学到了:

  1. 位运算:理解了按位与运算在标志检查中的应用
  2. 枚举:学会了如何给枚举成员分配显式值
  3. 结构体实现:掌握了如何为结构体实现方法
  4. 迭代器链:实践了使用 filter 和 collect 组合处理集合
  5. 实际应用:了解了位运算在现实世界中的用途

这些技能在实际开发中非常有用,特别是在处理权限系统、配置选项和状态管理时。位运算虽然看起来简单,但它是一种高效且强大的技术,是每个程序员都应该掌握的基础技能。

http://www.jsqmd.com/news/326499/

相关文章:

  • [品牌实战] 拿公模货怎么做出“品牌感”?浅析如何用 AI 批量清洗工厂 Logo,低成本打造私有 Listing
  • 岳阳英语雅思培训机构推荐。2026权威测评出国雅思辅导机构口碑榜
  • 常德英语雅思培训机构推荐。2026权威测评出国雅思辅导机构口碑榜
  • 强缓存失效了怎么办?深度解析浏览器内存缓存与硬盘缓存的存储逻辑
  • 岳阳英语雅思培训机构推荐,2026权威测评出国雅思辅导机构口碑榜
  • [蓝海掘金] 英语主图在墨西哥/越南卖不动?浅析如何用 AI 批量搞定“小语种”图片本地化,抢占流量洼地
  • 常德英语雅思培训机构推荐;2026权威测评出国雅思辅导机构口碑榜
  • 为什么 Webpack 要打包?从 HTTP/1.1 限制到 HTTP/2 多路复用原理详解
  • 2026牛栏网厂家推荐 高性价比防腐耐用
  • 搭子社交小程序功能设计:以兴趣为舟的轻盈同行
  • 类型擦除的优雅实现:C++ <any> 全面深度解析与运行时多态实战指南
  • 2026年市场一流护理床实力排行TOP10:优选推荐
  • 岳阳英语雅思培训机构推荐;2026权威测评出国雅思辅导机构口碑榜
  • 岳阳英语雅思培训机构推荐.2026权威测评出国雅思辅导机构口碑榜
  • 全网热议!2026年最佳激光二次元测量仪产品推荐,满足各类工业测量需求
  • 实力入围2026贵妇膏前TOP4!伊思芙透皮蛋白肽+虫草植萃,提亮不易反黑
  • 2026年四川可靠的GEO优化公司选哪家,SEO优化/网络公关/小红书代运营/GEO优化,GEO优化公司排行榜
  • 直播短视频系统源码,php获取具体日期的方法
  • 2026年分配器品牌制造商价格分析,看看哪家更划算?
  • 常德英语雅思培训机构推荐,2026权威测评出国雅思辅导机构口碑榜
  • 2026年知名护理床生产工厂TOP10盘点:高效解决方案
  • 漫谈ISO10605标准30kv静电放电发生器公司,哪家比较靠谱
  • 岳阳英语雅思培训机构推荐、2026权威测评出国雅思辅导机构口碑榜
  • php短视频源码,如何实现聚光加载效果?
  • 2026油头党清爽指南!控油蓬松去屑洗发水TOP榜权威实测
  • 2026年北京靠谱的大学食堂排烟系统清洗公司推荐,哪家性价比高?
  • 陪玩系统源码,redis发布与订阅的实现
  • 北美留学生必看!靠谱暑校转学分机构有哪些
  • 哪个暑校学费最便宜?深扒北美学分暑校留学生必看
  • 2026年割圈绒生产厂排名,经验丰富专业靠谱的厂家推荐