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

Rust 练习册 :Raindrops与FizzBuzz变体

Raindrops 是经典的 FizzBuzz 问题的一个有趣变体。在这个练习中,我们需要根据数字的因子来返回特定的字符串,而不是简单的数字。这不仅能帮助我们掌握条件判断和字符串拼接技巧,还能深入学习Rust中的模运算、字符串操作和控制流。

什么是 Raindrops?

Raindrops 是 FizzBuzz 问题的一个变体,规则如下:

  1. 如果数字能被 3 整除,返回 “Pling”
  2. 如果数字能被 5 整除,返回 “Plang”
  3. 如果数字能被 7 整除,返回 “Plong”
  4. 如果数字能被多个数整除,则按顺序拼接对应的字符串
  5. 如果数字不能被 3、5 或 7 整除,则返回数字本身

例如:

  • 1 → “1”
  • 3 → “Pling”
  • 5 → “Plang”
  • 7 → “Plong”
  • 15 → “PlingPlang”(能被3和5整除)
  • 105 → “PlingPlangPlong”(能被3、5和7整除)

让我们先看看练习提供的函数实现:

pub fn raindrops(n: u32) -> String {let mut res = String::new();if n % 3 == 0 {res.push_str("Pling");}if n % 5 == 0 {res.push_str("Plang");}if n % 7 == 0 {res.push_str("Plong");}if res.is_empty() {res.push_str(&n.to_string());}res
}

这是一个直接而清晰的实现,通过模运算检查数字是否能被特定数整除,并相应地构建结果字符串。

设计分析

1. 核心要求

  1. 模运算:正确使用模运算判断整除性
  2. 条件判断:根据不同的条件返回不同的字符串
  3. 字符串拼接:按顺序拼接多个字符串
  4. 数字转换:在不满足条件时将数字转换为字符串

2. 技术要点

  1. 控制流:使用 if 语句进行条件判断
  2. 字符串操作:使用 String 类型进行字符串构建
  3. 类型转换:将数字转换为字符串
  4. 逻辑组合:处理多个条件的组合情况

完整实现

1. 基础实现

pub fn raindrops(n: u32) -> String {let mut res = String::new();if n % 3 == 0 {res.push_str("Pling");}if n % 5 == 0 {res.push_str("Plang");}if n % 7 == 0 {res.push_str("Plong");}if res.is_empty() {res.push_str(&n.to_string());}res
}

2. 使用 match 的实现

pub fn raindrops(n: u32) -> String {let pling = if n % 3 == 0 { "Pling" } else { "" };let plang = if n % 5 == 0 { "Plang" } else { "" };let plong = if n % 7 == 0 { "Plong" } else { "" };let result = format!("{}{}{}", pling, plang, plong);if result.is_empty() {n.to_string()} else {result}
}

3. 使用迭代器的函数式实现

pub fn raindrops(n: u32) -> String {let factors = [(3, "Pling"), (5, "Plang"), (7, "Plong")];let result: String = factors.iter().filter(|&&(divisor, _)| n % divisor == 0).map(|&(_, sound)| sound).collect();if result.is_empty() {n.to_string()} else {result}
}

测试用例分析

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

#[test]
fn test_1() {assert_eq!("1", raindrops::raindrops(1));
}

数字1不能被3、5或7整除,所以返回"1"。

#[test]
fn test_3() {assert_eq!("Pling", raindrops::raindrops(3));
}

数字3能被3整除,所以返回"Pling"。

#[test]
fn test_5() {assert_eq!("Plang", raindrops::raindrops(5));
}

数字5能被5整除,所以返回"Plang"。

#[test]
fn test_7() {assert_eq!("Plong", raindrops::raindrops(7));
}

数字7能被7整除,所以返回"Plong"。

#[test]
fn test_6() {assert_eq!("Pling", raindrops::raindrops(6));
}

数字6能被3整除,所以返回"Pling"。

#[test]
fn test_8() {assert_eq!("8", raindrops::raindrops(8));
}

数字8不能被3、5或7整除,所以返回"8"。

#[test]
fn test_9() {assert_eq!("Pling", raindrops::raindrops(9));
}

数字9能被3整除,所以返回"Pling"。

#[test]
fn test_10() {assert_eq!("Plang", raindrops::raindrops(10));
}

数字10能被5整除,所以返回"Plang"。

#[test]
fn test_14() {assert_eq!("Plong", raindrops::raindrops(14));
}

数字14能被7整除,所以返回"Plong"。

#[test]
fn test_15() {assert_eq!("PlingPlang", raindrops::raindrops(15));
}

数字15能被3和5整除,所以返回"PlingPlang"。

#[test]
fn test_21() {assert_eq!("PlingPlong", raindrops::raindrops(21));
}

数字21能被3和7整除,所以返回"PlingPlong"。

#[test]
fn test_25() {assert_eq!("Plang", raindrops::raindrops(25));
}

数字25能被5整除,所以返回"Plang"。

#[test]
fn test_27() {assert_eq!("Pling", raindrops::raindrops(27));
}

数字27能被3整除,所以返回"Pling"。

#[test]
fn test_35() {assert_eq!("PlangPlong", raindrops::raindrops(35));
}

数字35能被5和7整除,所以返回"PlangPlong"。

#[test]
fn test_49() {assert_eq!("Plong", raindrops::raindrops(49));
}

数字49能被7整除,所以返回"Plong"。

#[test]
fn test_52() {assert_eq!("52", raindrops::raindrops(52));
}

数字52不能被3、5或7整除,所以返回"52"。

#[test]
fn test_105() {assert_eq!("PlingPlangPlong", raindrops::raindrops(105));
}

数字105能被3、5和7整除,所以返回"PlingPlangPlong"。

#[test]
fn test_3125() {assert_eq!("Plang", raindrops::raindrops(3125));
}

数字3125能被5整除,所以返回"Plang"。

#[test]
fn test_12121() {assert_eq!("12121", raindrops::raindrops(12_121));
}

数字12121不能被3、5或7整除,所以返回"12121"。

性能优化版本

考虑性能的优化实现:

pub fn raindrops(n: u32) -> String {// 预分配字符串以避免多次重新分配let mut res = String::with_capacity(16);if n % 3 == 0 {res.push_str("Pling");}if n % 5 == 0 {res.push_str("Plang");}if n % 7 == 0 {res.push_str("Plong");}if res.is_empty() {// 使用更高效的数字到字符串转换itoa::Buffer::new().format(n).to_string()} else {res}
}// 使用位运算的版本
pub fn raindrops_bitwise(n: u32) -> String {let mut flags = 0u8;if n % 3 == 0 { flags |= 1; }if n % 5 == 0 { flags |= 2; }if n % 7 == 0 { flags |= 4; }match flags {0 => n.to_string(),1 => "Pling".to_string(),2 => "Plang".to_string(),3 => "PlingPlang".to_string(),4 => "Plong".to_string(),5 => "PlingPlong".to_string(),6 => "PlangPlong".to_string(),7 => "PlingPlangPlong".to_string(),_ => unreachable!(),}
}// 使用查找表的版本
static SOUNDS: [&str; 8] = ["",           // 0: none"Pling",      // 1: 3"Plang",      // 2: 5"PlingPlang", // 3: 3,5"Plong",      // 4: 7"PlingPlong", // 5: 3,7"PlangPlong", // 6: 5,7"PlingPlangPlong", // 7: 3,5,7
];pub fn raindrops_lookup(n: u32) -> String {let mut index = 0;if n % 3 == 0 { index |= 1; }if n % 5 == 0 { index |= 2; }if n % 7 == 0 { index |= 4; }if index == 0 {n.to_string()} else {SOUNDS[index].to_string()}
}

错误处理和边界情况

考虑更多边界情况的实现:

#[derive(Debug, PartialEq)]
pub enum RaindropError {ZeroInput,
}impl std::fmt::Display for RaindropError {fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {match self {RaindropError::ZeroInput => write!(f, "输入不能为0"),}}
}impl std::error::Error for RaindropError {}pub fn raindrops(n: u32) -> String {// 处理0的特殊情况if n == 0 {return "0".to_string();}let mut res = String::new();if n % 3 == 0 {res.push_str("Pling");}if n % 5 == 0 {res.push_str("Plang");}if n % 7 == 0 {res.push_str("Plong");}if res.is_empty() {n.to_string()} else {res}
}// 返回Result的版本
pub fn raindrops_safe(n: u32) -> Result<String, RaindropError> {if n == 0 {Err(RaindropError::ZeroInput)} else {Ok(raindrops(n))}
}// 支持自定义因子的版本
pub struct RaindropConverter {factors: Vec<(u32, String)>,
}impl RaindropConverter {pub fn new() -> Self {RaindropConverter {factors: vec![(3, "Pling".to_string()),(5, "Plang".to_string()),(7, "Plong".to_string()),],}}pub fn with_factors(factors: Vec<(u32, String)>) -> Self {RaindropConverter { factors }}pub fn convert(&self, n: u32) -> String {let result: String = self.factors.iter().filter(|&&(divisor, _)| n % divisor == 0).map(|&(_, ref sound)| sound.as_str()).collect();if result.is_empty() {n.to_string()} else {result}}
}

扩展功能

基于基础实现,我们可以添加更多功能:

pub struct RaindropGame {score: u32,streak: u32,
}impl RaindropGame {pub fn new() -> Self {RaindropGame {score: 0,streak: 0,}}pub fn play_round(&mut self, number: u32, player_answer: &str) -> bool {let correct_answer = raindrops(number);let is_correct = correct_answer == player_answer;if is_correct {self.score += 1;self.streak += 1;} else {self.streak = 0;}is_correct}pub fn score(&self) -> u32 {self.score}pub fn streak(&self) -> u32 {self.streak}pub fn reset(&mut self) {self.score = 0;self.streak = 0;}
}// Raindrop分析器
pub struct RaindropAnalyzer;impl RaindropAnalyzer {pub fn new() -> Self {RaindropAnalyzer}pub fn analyze(&self, n: u32) -> RaindropAnalysis {let result = raindrops(n);let factors = self.get_factors(n);let is_prime = self.is_prime(n);RaindropAnalysis {number: n,result,factors,is_prime,}}fn get_factors(&self, n: u32) -> Vec<u32> {let mut factors = Vec::new();for i in 1..=n {if n % i == 0 {factors.push(i);}}factors}fn is_prime(&self, n: u32) -> bool {if n <= 1 {return false;}if n <= 3 {return true;}if n % 2 == 0 || n % 3 == 0 {return false;}let mut i = 5;while i * i <= n {if n % i == 0 || n % (i + 2) == 0 {return false;}i += 6;}true}
}pub struct RaindropAnalysis {pub number: u32,pub result: String,pub factors: Vec<u32>,pub is_prime: bool,
}// 批量处理版本
pub fn raindrops_batch(numbers: &[u32]) -> Vec<String> {numbers.iter().map(|&n| raindrops(n)).collect()
}// 生成Raindrop序列
pub fn raindrop_sequence(start: u32, count: usize) -> Vec<String> {(start..start + count as u32).map(raindrops).collect()
}// 便利函数
pub fn raindrops(n: u32) -> String {let mut res = String::new();if n % 3 == 0 {res.push_str("Pling");}if n % 5 == 0 {res.push_str("Plang");}if n % 7 == 0 {res.push_str("Plong");}if res.is_empty() {n.to_string()} else {res}
}pub fn format_raindrop_table(start: u32, end: u32) -> String {(start..=end).map(|n| format!("{}: {}", n, raindrops(n))).collect::<Vec<_>>().join("\n")
}

实际应用场景

Raindrops 在实际开发中有以下应用:

  1. 教育软件:编程入门教学和练习工具
  2. 游戏开发:益智游戏和儿童教育游戏
  3. 面试题目:技术面试中的经典编程题
  4. 算法练习:条件判断和控制流练习
  5. 测试框架:单元测试和功能测试示例
  6. 代码示例:教学和演示中的简单示例
  7. 逻辑训练:编程逻辑思维训练
  8. 儿童应用:儿童编程启蒙应用

算法复杂度分析

  1. 时间复杂度:O(1)

    • 只需要进行固定次数的模运算和字符串操作
  2. 空间复杂度:O(1)

    • 只需要常数级别的额外空间存储结果

与其他实现方式的比较

// 使用递归的实现
pub fn raindrops_recursive(n: u32) -> String {fn convert(n: u32, divisors: &[(u32, &str)]) -> String {if divisors.is_empty() {return if n.to_string().is_empty() { n.to_string() } else { String::new() };}let (divisor, sound) = divisors[0];let mut result = if n % divisor == 0 { sound.to_string() } else { String::new() };result.push_str(&convert(n, &divisors[1..]));result}let divisors = [(3, "Pling"), (5, "Plang"), (7, "Plong")];let result = convert(n, &divisors);if result.is_empty() {n.to_string()} else {result}
}// 使用宏的实现
macro_rules! raindrops {($n:expr) => {{let mut res = String::new();if $n % 3 == 0 {res.push_str("Pling");}if $n % 5 == 0 {res.push_str("Plang");}if $n % 7 == 0 {res.push_str("Plong");}if res.is_empty() {res.push_str(&format!("{}", $n));}res}};
}// 使用第三方库的实现
// [dependencies]
// num = "0.4"use num::Integer;pub fn raindrops_with_num(n: u32) -> String {let mut res = String::new();if n.is_multiple_of(&3) {res.push_str("Pling");}if n.is_multiple_of(&5) {res.push_str("Plang");}if n.is_multiple_of(&7) {res.push_str("Plong");}if res.is_empty() {n.to_string()} else {res}
}// 使用函数式组合的实现
pub fn raindrops_functional(n: u32) -> String {[(3, "Pling"), (5, "Plang"), (7, "Plong")].iter().filter_map(|&(divisor, sound)| {if n % divisor == 0 {Some(sound)} else {None}}).collect::<String>().or_else(|| n.to_string())
}

总结

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

  1. 条件判断:掌握了使用 if 语句进行多条件判断
  2. 模运算:深入理解了模运算在整除性判断中的应用
  3. 字符串操作:学会了构建和拼接字符串
  4. 控制流:理解了不同控制流结构的使用场景
  5. 性能优化:学会了预分配内存和使用高效算法等优化技巧
  6. 扩展设计:理解了如何设计可扩展的系统架构

这些技能在实际开发中非常有用,特别是在教育软件、游戏开发、算法练习等场景中。Raindrops虽然是一个简单的编程练习,但它涉及到了条件判断、模运算、字符串操作、控制流等许多核心概念,是学习Rust入门编程的良好起点。

通过这个练习,我们也看到了Rust在简单算法实现方面的简洁性和高效性,以及如何用安全且高效的方式实现经典的编程问题。这种结合了安全性和性能的语言特性正是Rust的魅力所在。

http://www.dtcms.com/a/585345.html

相关文章:

  • Home Assistant Z-Pi 800全新硬件打造远距离智能家居体验
  • 易货电商新模式:小本钱撬动大市场,破解实体店“三大愁”
  • 做网站需要多少人网站建设价格很 好乐云seo
  • Java 大视界 -- 基于 Java 的大数据可视化在城市空气质量监测与污染溯源中的应用
  • 《玩转Docker》[应用篇16]:Docker安装部署HomeBox-家庭库存管理
  • 网站用什么布局wordpress怎么使用七牛云储存
  • Cursor接入Figma并使用的图文教程
  • Java基础——集合进阶4
  • CUDA C++编程指南(3.2.7)——内存同步域
  • 超市进销存源码
  • 100G相机接口
  • Linux第一个小程序 之 【进度条】
  • 网站建设的目的及功能定位网站建设落地页
  • CentOS 7 上安装 MySQL 8.0
  • LLM 训练基础概念与流程简介
  • HTML 音频(Audio)详解
  • 认识BUG~
  • RV1126 NO.44:OPENCV的cvtColor和putText的讲解
  • HTTP-发展史
  • AI 编程工具Claude Code 介绍
  • 2022年IEEE TITS SCI2区TOP,基于切线交点和目标引导策略的无人机自主路径规划,深度解析+性能实测
  • 旧电脑系统无损迁移至新电脑、硬盘系统克隆完整教程
  • 长沙网站seo诊断佛山做网站企业
  • 识别和破除信息茧房
  • 超时重传 vs 快速重传:TCP双保险如何拯救网络丢包?
  • 余弦相似度:衡量向量空间方向一致性的核心度量
  • 好网站建设公司报价文字类wordpress主题
  • 【科研绘图系列】R语言绘制密度分布图(density plot)
  • R语言绘图与可视化第六章总结
  • 建设工程消防设计备案网站网络服务提供者收集和使用