🎯 FizzBuzz Challenge
🎯 Mục Tiêu Dự Án
FizzBuzz là một bài toán lập trình kinh điển, thường được dùng trong phỏng vấn! Nhiệm vụ:
In ra các số từ 1 đến N, nhưng:
- Nếu số chia hết cho 3: In
Fizz - Nếu số chia hết cho 5: In
Buzz - Nếu số chia hết cho cả 3 và 5: In
FizzBuzz - Các trường hợp khác: In số đó
Ví dụ (1-15):
1, 2, Fizz, 4, Buzz, Fizz, 7, 8, Fizz, Buzz, 11, Fizz, 13, 14, FizzBuzz
Bạn Sẽ Học Được
- ✅ Sử dụng loops hiệu quả
- ✅ Điều kiện với modulo operator
% - ✅ Thứ tự ưu tiên của điều kiện
- ✅ Refactoring code
- ✅ Viết tests
📦 Bước 1: Tạo Project
cargo new fizzbuzz
cd fizzbuzz
🎮 Bước 2: Version 1 - Classic FizzBuzz
fn main() {
println!("🎯 FizzBuzz Challenge");
println!("====================\n");
for i in 1..=100 {
if i % 15 == 0 {
println!("FizzBuzz");
} else if i % 3 == 0 {
println!("Fizz");
} else if i % 5 == 0 {
println!("Buzz");
} else {
println!("{}", i);
}
}
}
Tại Sao Kiểm Tra 15 Trước?
// ❌ SAI: Kiểm tra 3 trước
if i % 3 == 0 {
println!("Fizz"); // 15 sẽ in "Fizz" và dừng lại!
} else if i % 5 == 0 {
println!("Buzz");
} else if i % 15 == 0 {
println!("FizzBuzz"); // Không bao giờ đến đây!
}
// ✅ ĐÚNG: Kiểm tra 15 trước
if i % 15 == 0 {
println!("FizzBuzz");
} else if i % 3 == 0 {
println!("Fizz");
} else if i % 5 == 0 {
println!("Buzz");
}
🎨 Bước 3: Version 2 - Với Functions
fn main() {
println!("🎯 FizzBuzz Challenge - Version 2");
println!("==================================\n");
for i in 1..=100 {
println!("{}", fizzbuzz(i));
}
}
fn fizzbuzz(n: u32) -> String {
match (n % 3 == 0, n % 5 == 0) {
(true, true) => "FizzBuzz".to_string(),
(true, false) => "Fizz".to_string(),
(false, true) => "Buzz".to_string(),
(false, false) => n.to_string(),
}
}
Match với tuple (bool, bool) giúp code ngắn gọn và rõ ràng hơn!
🎨 Bước 4: Version 3 - Tương Tác
Cho phép người dùng chọn số lượng:
use std::io;
fn main() {
println!("🎯 FizzBuzz Challenge - Interactive");
println!("===================================\n");
loop {
println!("📋 Menu:");
println!(" 1. FizzBuzz kinh điển (1-100)");
println!(" 2. Tùy chỉnh phạm vi");
println!(" 3. Thoát");
let choice = get_input("\n📝 Chọn: ");
match choice.trim() {
"1" => print_fizzbuzz(1, 100),
"2" => custom_fizzbuzz(),
"3" => {
println!("👋 Tạm biệt!");
break;
},
_ => println!("⚠️ Lựa chọn không hợp lệ!"),
}
}
}
fn print_fizzbuzz(start: u32, end: u32) {
println!("\n🎯 FizzBuzz từ {} đến {}:", start, end);
println!("─────────────────────────────");
for i in start..=end {
print!("{:<12}", fizzbuzz(i));
// In 5 số mỗi dòng
if (i - start + 1) % 5 == 0 {
println!();
}
}
println!("\n");
}
fn custom_fizzbuzz() {
let start = match get_number("📝 Số bắt đầu: ") {
Some(n) if n > 0 => n,
_ => {
println!("⚠️ Số phải lớn hơn 0!");
return;
}
};
let end = match get_number("📝 Số kết thúc: ") {
Some(n) if n >= start => n,
_ => {
println!("⚠️ Số kết thúc phải >= số bắt đầu!");
return;
}
};
print_fizzbuzz(start, end);
}
fn fizzbuzz(n: u32) -> String {
match (n % 3 == 0, n % 5 == 0) {
(true, true) => "FizzBuzz".to_string(),
(true, false) => "Fizz".to_string(),
(false, true) => "Buzz".to_string(),
(false, false) => n.to_string(),
}
}
fn get_input(prompt: &str) -> String {
use std::io::Write;
print!("{}", prompt);
std::io::stdout().flush().unwrap();
let mut input = String::new();
io::stdin()
.read_line(&mut input)
.expect("❌ Không đọc được input!");
input
}
fn get_number(prompt: &str) -> Option<u32> {
let input = get_input(prompt);
match input.trim().parse::<u32>() {
Ok(n) => Some(n),
Err(_) => {
println!("⚠️ Vui lòng nhập số hợp lệ!");
None
}
}
}
🎨 Bước 5: Version 4 - Customizable
Cho phép tùy chỉnh các số chia:
use std::io;
struct FizzBuzzRule {
divisor: u32,
word: String,
}
impl FizzBuzzRule {
fn new(divisor: u32, word: &str) -> Self {
FizzBuzzRule {
divisor,
word: word.to_string(),
}
}
}
fn main() {
println!("🎯 FizzBuzz Challenge - Customizable");
println!("====================================\n");
// Quy tắc mặc định
let mut rules = vec![
FizzBuzzRule::new(3, "Fizz"),
FizzBuzzRule::new(5, "Buzz"),
];
loop {
println!("\n📋 Menu:");
println!(" 1. Chạy FizzBuzz (1-100)");
println!(" 2. Xem quy tắc hiện tại");
println!(" 3. Thêm quy tắc mới");
println!(" 4. Reset quy tắc mặc định");
println!(" 5. Thoát");
let choice = get_input("\n📝 Chọn: ");
match choice.trim() {
"1" => run_fizzbuzz(&rules, 1, 100),
"2" => show_rules(&rules),
"3" => add_rule(&mut rules),
"4" => {
rules = vec![
FizzBuzzRule::new(3, "Fizz"),
FizzBuzzRule::new(5, "Buzz"),
];
println!("✅ Đã reset về quy tắc mặc định!");
},
"5" => {
println!("👋 Tạm biệt!");
break;
},
_ => println!("⚠️ Lựa chọn không hợp lệ!"),
}
}
}
fn run_fizzbuzz(rules: &[FizzBuzzRule], start: u32, end: u32) {
println!("\n🎯 Kết quả FizzBuzz:");
println!("─────────────────────");
for i in start..=end {
let result = apply_rules(i, rules);
print!("{:<15}", result);
if (i - start + 1) % 5 == 0 {
println!();
}
}
println!("\n");
}
fn apply_rules(n: u32, rules: &[FizzBuzzRule]) -> String {
let mut result = String::new();
for rule in rules {
if n % rule.divisor == 0 {
result.push_str(&rule.word);
}
}
if result.is_empty() {
n.to_string()
} else {
result
}
}
fn show_rules(rules: &[FizzBuzzRule]) {
println!("\n📜 Quy tắc hiện tại:");
for (i, rule) in rules.iter().enumerate() {
println!(" {}. Chia hết cho {} → {}", i + 1, rule.divisor, rule.word);
}
}
fn add_rule(rules: &mut Vec<FizzBuzzRule>) {
println!("\n➕ Thêm quy tắc mới");
let divisor = match get_number("📝 Số chia: ") {
Some(n) if n > 0 => n,
_ => {
println!("⚠️ Số chia phải > 0!");
return;
}
};
let word = get_input("📝 Từ thay thế: ").trim().to_string();
if word.is_empty() {
println!("⚠️ Từ không được rỗng!");
return;
}
rules.push(FizzBuzzRule::new(divisor, &word));
println!("✅ Đã thêm quy tắc: {} → {}", divisor, word);
}
fn get_input(prompt: &str) -> String {
use std::io::Write;
print!("{}", prompt);
std::io::stdout().flush().unwrap();
let mut input = String::new();
io::stdin()
.read_line(&mut input)
.expect("❌ Không đọc được input!");
input
}
fn get_number(prompt: &str) -> Option<u32> {
let input = get_input(prompt);
match input.trim().parse::<u32>() {
Ok(n) => Some(n),
Err(_) => None,
}
}
Ví dụ với quy tắc tùy chỉnh:
- Chia hết cho 3 → "Fizz"
- Chia hết cho 5 → "Buzz"
- Chia hết cho 7 → "Boom"
Số 105 (chia hết cho cả 3, 5, 7) → "FizzBuzzBoom"
🧪 Bước 6: Thêm Unit Tests
fn fizzbuzz(n: u32) -> String {
match (n % 3 == 0, n % 5 == 0) {
(true, true) => "FizzBuzz".to_string(),
(true, false) => "Fizz".to_string(),
(false, true) => "Buzz".to_string(),
(false, false) => n.to_string(),
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_normal_numbers() {
assert_eq!(fizzbuzz(1), "1");
assert_eq!(fizzbuzz(2), "2");
assert_eq!(fizzbuzz(4), "4");
}
#[test]
fn test_fizz() {
assert_eq!(fizzbuzz(3), "Fizz");
assert_eq!(fizzbuzz(6), "Fizz");
assert_eq!(fizzbuzz(9), "Fizz");
}
#[test]
fn test_buzz() {
assert_eq!(fizzbuzz(5), "Buzz");
assert_eq!(fizzbuzz(10), "Buzz");
assert_eq!(fizzbuzz(20), "Buzz");
}
#[test]
fn test_fizzbuzz() {
assert_eq!(fizzbuzz(15), "FizzBuzz");
assert_eq!(fizzbuzz(30), "FizzBuzz");
assert_eq!(fizzbuzz(45), "FizzBuzz");
}
#[test]
fn test_large_numbers() {
assert_eq!(fizzbuzz(300), "FizzBuzz");
assert_eq!(fizzbuzz(301), "301");
}
}
Chạy tests:
cargo test
Output:
running 5 tests
test tests::test_buzz ... ok
test tests::test_fizz ... ok
test tests::test_fizzbuzz ... ok
test tests::test_large_numbers ... ok
test tests::test_normal_numbers ... ok
test result: ok. 5 passed; 0 failed; 0 ignored; 0 measured
📊 Phân Tích Hiệu Suất
Cách 1: If-Else Chain
// Đơn giản, dễ đọc
if i % 15 == 0 {
"FizzBuzz"
} else if i % 3 == 0 {
"Fizz"
} else if i % 5 == 0 {
"Buzz"
} else {
// number
}
Cách 2: Match Tuple
// Ngắn gọn, functional
match (n % 3 == 0, n % 5 == 0) {
(true, true) => "FizzBuzz",
(true, false) => "Fizz",
(false, true) => "Buzz",
(false, false) => // number
}
Cách 3: String Building
// Linh hoạt, mở rộng dễ
let mut result = String::new();
if n % 3 == 0 { result.push_str("Fizz"); }
if n % 5 == 0 { result.push_str("Buzz"); }
if result.is_empty() { result = n.to_string(); }
result
💪 Thử Thách Nâng Cao
Thử Thách 1: FizzBuzzBazz
Thêm quy tắc:
- Chia hết cho 7 → "Bazz"
Số 105 (3×5×7) → "FizzBuzzBazz"
Thử Thách 2: Reverse FizzBuzz
Cho chuỗi "Fizz, Buzz, 3", tìm số ban đầu (1, 2, 3)
💡 Gợi ý
fn reverse_fizzbuzz(s: &str) -> Option<u32> {
match s {
"Fizz" => {
// Có thể là 3, 6, 9, 12 (không phải bội của 5)
None // Không xác định được chính xác
},
"FizzBuzz" => {
// Bội của 15
None // Cần thêm thông tin
},
_ => s.parse().ok() // Nếu là số
}
}
Thử Thách 3: FizzBuzz Generator
Tạo iterator vô hạn:
struct FizzBuzzIterator {
current: u32,
}
impl Iterator for FizzBuzzIterator {
type Item = String;
fn next(&mut self) -> Option<Self::Item> {
self.current += 1;
Some(fizzbuzz(self.current))
}
}
// Sử dụng
let fizzer = FizzBuzzIterator { current: 0 };
for item in fizzer.take(20) {
println!("{}", item);
}
Thử Thách 4: FizzBuzz Benchmark
Đo tốc độ các cách implement khác nhau:
[dev-dependencies]
criterion = "0.5"
use criterion::{black_box, criterion_group, criterion_main, Criterion};
fn fizzbuzz_v1(n: u32) -> String {
// Implementation 1
}
fn fizzbuzz_v2(n: u32) -> String {
// Implementation 2
}
fn benchmark_fizzbuzz(c: &mut Criterion) {
c.bench_function("fizzbuzz v1", |b| {
b.iter(|| {
for i in 1..=100 {
black_box(fizzbuzz_v1(i));
}
})
});
c.bench_function("fizzbuzz v2", |b| {
b.iter(|| {
for i in 1..=100 {
black_box(fizzbuzz_v2(i));
}
})
});
}
criterion_group!(benches, benchmark_fizzbuzz);
criterion_main!(benches);
🎓 Biến Thể FizzBuzz
FizzBuzz SQL
SELECT
CASE
WHEN n % 15 = 0 THEN 'FizzBuzz'
WHEN n % 3 = 0 THEN 'Fizz'
WHEN n % 5 = 0 THEN 'Buzz'
ELSE CAST(n AS VARCHAR)
END
FROM numbers;
FizzBuzz Regex
Kiểm tra output có đúng format không:
use regex::Regex;
fn validate_fizzbuzz_output(output: &str) -> bool {
let re = Regex::new(r"^(Fizz|Buzz|FizzBuzz|\d+)$").unwrap();
re.is_match(output)
}
🐛 Lỗi Thường Gặp
Lỗi 1: Sai Thứ Tự
// ❌ SAI
if i % 3 == 0 { return "Fizz"; } // 15 sẽ return sớm!
if i % 15 == 0 { return "FizzBuzz"; } // Không bao giờ đến
// ✅ ĐÚNG
if i % 15 == 0 { return "FizzBuzz"; }
if i % 3 == 0 { return "Fizz"; }
Lỗi 2: Quên Return String
// ❌ SAI: Trả về &str, expect String
fn fizzbuzz(n: u32) -> String {
if n % 3 == 0 { "Fizz" } // Type mismatch!
}
// ✅ ĐÚNG
fn fizzbuzz(n: u32) -> String {
if n % 3 == 0 { "Fizz".to_string() }
}
📚 Kiến Thức Đã Học
✅ Loops: For loops với ranges
✅ Modulo: Operator % để kiểm tra chia hết
✅ Conditional Logic: If-else và match
✅ Pattern Matching: Match với tuples
✅ String Building: Tạo strings động
✅ Testing: Viết unit tests
✅ Refactoring: Cải thiện code structure
🎯 Bước Tiếp Theo
Bạn đã hoàn thành tất cả Beginner Projects! 🎉
Sẵn sàng cho thử thách lớn hơn?
➡️ Intermediate Projects: Todo CLI App ➡️ Practice: Basic Exercises
🎉 Chúc mừng! Bạn đã chinh phục FizzBuzz! 🎯