Hi,大家好,这里是炼手Rust专栏,我是xiaoK,今天来看个rust语言设计相关的挑战:你对什么过敏。
提问
阿峰对很多东西过敏,他去医院诊断,拿到一个过敏评分,我们需要确定阿峰是否对特定物品过敏,以及阿峰的过敏物品完整列表。
过敏测试会产生一个单一的数字分数,其中包含阿峰的所有过敏的信息。
已测试的项目列表为:
鸡蛋 (1)
花生 (2)
贝类 (4)
草莓 (8)
西红柿 (16)
巧克力 (32)
花粉 (64)
猫 (128)
因此,如果阿峰对花生和巧克力过敏,他的得分为 34。
现在,给出 34 分,我们的程序应该能够说:
阿峰是否对上面列出的任何一种过敏原过敏。
阿峰的所有过敏原。
注意:给定分数可能包括上面未列出的过敏原(即分数为 256、512、1024 等的过敏原)。我们的程序应该忽略这些。例如,如果过敏评分为 257,则我们程序应仅报告鸡蛋 (1) 过敏。
模板:
pub struct Allergies;
#[derive(Debug, PartialEq, Eq)]
pub enum Allergen {
Eggs,
Peanuts,
Shellfish,
Strawberries,
Tomatoes,
Chocolate,
Pollen,
Cats,
}
impl Allergies {
pub fn new(score: u32) -> Self {
todo!("Given the '{score}' score, construct a new Allergies struct.");
}
pub fn is_allergic_to(&self, allergen: &Allergen) -> bool {
todo!("Determine if the patient is allergic to the '{allergen:?}' allergen.");
}
pub fn allergies(&self) -> Vec<Allergen> {
todo!("Return the list of allergens contained within the score with which the Allergies struct was made.");
}
}
分析
这是一个非常典型的程序设计挑战,并不着重于算法的考察,而是考察程序设计的逻辑。
按照题设,我们不难发现,算法上该挑战其实是考察位运算的应用。Eggs为 1,Peanuts为 2,即1 << n - 1,判断是否对过敏原过敏,可以使用与运算a & b即可。
解决方案
pub struct Allergies(u32);
#[derive(Debug, PartialEq, Eq)]
pub enum Allergen {
Eggs,
Peanuts,
Shellfish,
Strawberries,
Tomatoes,
Chocolate,
Pollen,
Cats,
}
impl Allergen {
pub fn ordinal(&self) -> u32 {
match self {
Allergen::Eggs => 0,
Allergen::Peanuts => 1,
Allergen::Shellfish => 2,
Allergen::Strawberries => 3,
Allergen::Tomatoes => 4,
Allergen::Chocolate => 5,
Allergen::Pollen => 6,
Allergen::Cats => 7,
}
}
pub fn values() -> 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 {
Self(score)
}
pub fn is_allergic_to(&self, allergen: &Allergen) -> bool {
((1 << allergen.ordinal()) & self.0) != 0
}
pub fn allergies(&self) -> Vec<Allergen> {
let mut all = Allergen::values();
let all = all.into_iter().filter(|s| self.is_allergic_to(s)).collect();
all
}
}
Trick
- 此处我们创建了一个方法
ordinal返回相关的枚举值,当然在rust中也可以直接定义枚举的值,这个读者可以自行选择。 - 过滤使用
filter,传入一个lambda表达式作为判断依据。 - 如果想简单的列出枚举的所有值和序数
ordinal,可以借助第三方库enum_ordinalize,读者有空可以自行使用。