FizzBuzz Can Finally Be Implemented in Stable Rust

trait Monoid {
// don't have assoc. values yet, so us a nullary function
fn id() -> Self;
// an associative binary operation
// this version consumes arguments
// a non-consuming version might be possible
fn op(self, other: Self) -> Self;
}

// owned strings implement append
impl Monoid for ~str {
fn id() -> ~str { ~"" } // identity is empty string
fn op(self, other: ~str) -> ~str {
self.append(other)
}
}

// not sure if we can impl Monoid for &str

// Options are Monoids if they contain Monoids
impl<A: Monoid> Monoid for Option<A> {
fn id() -> Option<A> { None }
fn op(self, other: Option<A>) -> Option<A> {
match (self, other) {
(None, b) => b,
(a, None) => a,
(Some(a), Some(b)) => Some(a.op(b)),
}
}
}

fn fizzbuzz(i: int) -> ~str {
// filtered is the equivalent a comprehension guard
// unwrap_or is fromMaybe
Some(~"fizz").filtered(|_| i % 3 == 0).op(
Some(~"buzz").filtered(|_| i % 5 == 0)
// we can add more conditions by appending
// a .op( above and inserting a new line below
).unwrap_or(i.to_str())
}

fn main() {
let args = std::os::args();

match from_str::<int>(args[1]){
Some(x)=>for i in std::iter::range_inclusive(1, x) {
println!("{}", fizzbuzz(i));
},
None=>println!("I need a real number")
}
}
fn fizzbuzz(i: int) -> String {
// filtered is the equivalent a comprehension guard
// unwrap_or is fromMaybe
fizzbuzz_op(i, None,
[("Fizz".to_string(), 3), ("Buzz".to_string(), 5), ("Bazz".to_string(), 7)]
).unwrap_or(i.to_str())
}

fn fizzbuzz_op(i: int, res: Option<String>, rest: &[(String, int)]) -> Option<String> {
match rest {
[ref first, .. tail] => fizzbuzz_op(
i,
res.op(Some(first.ref0().clone()).filtered(|_| i % *first.ref1() == 0)),
tail
),
_ => res
}
}
pub fn op_filter(tuples: &[(&'static str, &Fn() -> bool)]) -> Option<String> {
tuples.iter().fold(None, |res, &(value, include)|
res.op(utils::filter(Some(value.to_string()), include()))
)
}
for i in 1..n.checked_add(1).expect("Integer overflow")  { ... }
//does the monoid operation on the slice of tuples if the closure evaluates to true
fn accumulate<'a, T: Monoid>(tuples: &[(&'a str, &Fn(i32) -> bool)], i: i32) -> Option<T>
where &'a str: Into<T> {

tuples.iter()
.filter(|&x|second(x)(i)) //don't try to make this point-free it's point-less
.map(first)
.cloned()
.map(<&str>::into)
.fold1(T::op)

//op just concatenates, but Cow<'a, str> does not satisfy Add
}
//does the monoid operation on the slice of tuples if the closure evaluates to true
fn accumulate<'a, T: Monoid>(tuples: &[(&'a str, &Fn(i32) -> bool)], i: i32) -> T
where T: From<&'a str> + From<String> {

tuples.iter()
.filter(apply(second, i))
.map(first)
.cloned()
.map(<&str>::into)
.fold1(T::op)
.unwrap_or_else(|| i.to_string().into())
//op just concatenates, but String does not satisfy Add
}

fn apply<A, B, C, F, G>(mut f: F, a: A)
-> impl FnMut(&B) -> C // must still be `for<'r> impl FnMut(&'r B) -> C`, because that’s what filter requires
where F: FnMut(B) -> G, // must not be `for<'r> FnMut(&'r B) -> G`, because regular functions do not implement it
G: FnMut(A) -> C,
B: Copy, // for dereferencing
A: Clone {

move |b| f(*b)(a.clone()) // this must do any bridging necessary to satisfy the requirements
}
    let acc = (1..=15).map(|i| fizzbuzz::fizzbuzz(&[
("Fizz", &|i: i32| i % 3 == 0),
("Buzz", &|i: i32| i % 5 == 0),
], i)).collect::<Vec<_>>().join(" ");

assert_eq!(acc, "1 2 Fizz 4 Buzz Fizz 7 8 Fizz Buzz 11 Fizz 13 14 FizzBuzz".to_string());

--

--

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store