From ac194ed95d6b97eef6dd60f8a3524f63fa408553 Mon Sep 17 00:00:00 2001 From: Federico I Date: Tue, 17 Mar 2020 00:53:30 +0000 Subject: [rust] Beer Song --- beer-song/src/intersperse.rs | 52 ++++++++++++++++++++++++++++++++++++++++++++ beer-song/src/lib.rs | 20 +++++++++++++++++ 2 files changed, 72 insertions(+) create mode 100644 beer-song/src/intersperse.rs create mode 100644 beer-song/src/lib.rs (limited to 'beer-song/src') diff --git a/beer-song/src/intersperse.rs b/beer-song/src/intersperse.rs new file mode 100644 index 0000000..51580e6 --- /dev/null +++ b/beer-song/src/intersperse.rs @@ -0,0 +1,52 @@ +use std::rc::Rc; + +/// An iterator that allows to insert a given element `elem` every `interval` elements of the input +/// iterator. +/// +/// The interleaved element is wrapped in a `Rc` smart pointer to avoid hard copies. +pub struct Intersperse { + inner: std::iter::Peekable, // required to have `peek(..)` + elem: Rc, + interval: u32, + intersperse: u32, +} + +impl Intersperse { + pub fn new(inner: I, elem: I::Item, interval: u32) -> Intersperse { + Intersperse { + inner: inner.peekable(), + elem: Rc::new(elem), + interval: interval + 1, // `+ 1` needed to align semantics and implementation + intersperse: 0, + } + } +} + +impl Iterator for Intersperse { + type Item = Rc; + + fn next(&mut self) -> Option { + // Counter rotation + self.intersperse = (self.intersperse + 1) % self.interval; + // Insert a copy of the element only when counter goes to 0 and we haven't reached the end + // of the inner iterator. + if self.intersperse == 0 && self.inner.peek().is_some() { + Some(Rc::clone(&self.elem)) + } else { + self.inner.next() + .map(|x| Rc::new(x)) // Go from Option to Option> + } + } +} + +/// Extend `Iterator`s with `intersperse` function. +pub trait IntersperseIterator: Iterator { + fn intersperse(self, elem: Self::Item, interval: u32) -> Intersperse + where + Self: Sized, + { + Intersperse::new(self, elem, interval) + } +} + +impl IntersperseIterator for I {} diff --git a/beer-song/src/lib.rs b/beer-song/src/lib.rs new file mode 100644 index 0000000..990fbbf --- /dev/null +++ b/beer-song/src/lib.rs @@ -0,0 +1,20 @@ +use itertools::Itertools; + +pub fn verse(n: u32) -> String { + match n { + 0 => format!( "No more bottles of beer on the wall, no more bottles of beer.\n\ + Go to the store and buy some more, 99 bottles of beer on the wall.\n"), + 1 => format!( "1 bottle of beer on the wall, 1 bottle of beer.\n\ + Take it down and pass it around, no more bottles of beer on the wall.\n"), + 2 => format!( "2 bottles of beer on the wall, 2 bottles of beer.\n\ + Take one down and pass it around, 1 bottle of beer on the wall.\n"), + _ => format!( "{} bottles of beer on the wall, {} bottles of beer.\n\ + Take one down and pass it around, {} bottles of beer on the wall.\n", + n, n, n - 1) + } +} + +pub fn sing(start: u32, end: u32) -> String { + // Note: call to `join` can be substituted with `.intersperse(String::from("\n")).collect()` + (end..=start).rev().map(|x| verse(x)).join("\n") +} -- cgit v1.2.3