SixArm / rust-quick-guide

Rust Quick Guide provides short introductions to many Rust programming topics, including the language, ecosystem, concepts, crates, and more.
Other
49 stars 2 forks source link

FizzBuzz #1

Open ErikLambrechts opened 1 year ago

ErikLambrechts commented 1 year ago

This is a nice project. I just would like to add that the FizzBuzz example could be done with a match construction. So it may not be the best example of control flow. Here is my take on the FizzBuzz code

fn main() {
    for i in 1..=100 {
        match i {
            i if i % 3 == 0 && i % 5 == 0 => println!("FizzBuzz"),
            i if i % 3 == 0 => println!("Fizz"),
            i if i % 5 == 0 => println!("Buzz"),
            i => println!("{}", i),
        }
    }
}
joelparkerhenderson commented 1 year ago

Great point. I'll add that to the next version.

You're inspiring me to use FizzBuzz as a way to explain generic traits too... something like this...

pub trait FizzBuzz {
    fn fizzbuzz(&self) -> String;
}

impl<T> FizzBuzz for T
where 
    T: num::traits::int::PrimInt,
    T: std::fmt::Display,
{
    fn fizzbuzz(&self) -> String {
        let t0 = T::zero();
        let t3 = T::from(3).unwrap();
        let t5 = T::from(5).unwrap();
        match (*self % t3 == t0, *self % t5 == t0) {
            (true, true) => String::from("FizzBuzz"),
            (true, _) => String::from("Fizz"),
            (_, true) => String::from("Buzz"),
            _ => format!("{}", self),
        }
    }
}

fn main() {
    for i in 1..=100 {
        println!("{}", i.fizzbuzz())
    }
}
ErikLambrechts commented 1 year ago

Great example.

Here is my take on FizzBuzz using some other features

Using iterators and closures

fn main() {
    let max_iterations = 100;

    (1..=max_iterations).map(|n| {
        match (n % 3, n % 5) {
            (0, 0) => "FizzBuzz".to_string(),
            (0, _) => "Fizz".to_string(),
            (_, 0) => "Buzz".to_string(),
            _ => n.to_string(),
        }
    }).for_each(|result| println!("{}", result));
}

Implementing your own iterator

struct FizzBuzz {
    current: u32,
    max: u32,
}

impl FizzBuzz {
    fn new(max: u32) -> Self {
        Self {
            current: 1,
            max,
        }
    }
}

impl Iterator for FizzBuzz {
    type Item = String;

    fn next(&mut self) -> Option<Self::Item> {
        if self.current > self.max {
            return None;
        }

        let result = match (self.current % 3 == 0, self.current % 5 == 0) {
            (true, true) => "FizzBuzz".to_string(),
            (true, false) => "Fizz".to_string(),
            (false, true) => "Buzz".to_string(),
            _ => self.current.to_string(),
        };

        self.current += 1;
        Some(result)
    }
}

fn main() {
    for fb in FizzBuzz::new(100) {
        println!("{}", fb);
    }
}
joelparkerhenderson commented 1 year ago

That's great! What a fun code golf. :-)

I wonder how much faster the code could be with refactoring the to_string to a constant str slice, or to a clone-on-write String.

Or how much faster the code could be with pre-filled 15-item array of Option, and an index = n % 15 that returns Some(n) => n, or None => n as a string?