diff --git a/config.json b/config.json index a90b1f05f..0775fa7fd 100644 --- a/config.json +++ b/config.json @@ -24,6 +24,7 @@ "grade-school", "tournament", "robot-simulator", + "bracket-push", "queen-attack", "sublist", "space-age", diff --git a/exercises/bracket-push/Cargo.lock b/exercises/bracket-push/Cargo.lock new file mode 100644 index 000000000..11412f139 --- /dev/null +++ b/exercises/bracket-push/Cargo.lock @@ -0,0 +1,4 @@ +[root] +name = "bracket-push" +version = "0.0.0" + diff --git a/exercises/bracket-push/Cargo.toml b/exercises/bracket-push/Cargo.toml new file mode 100644 index 000000000..198b1bdab --- /dev/null +++ b/exercises/bracket-push/Cargo.toml @@ -0,0 +1,3 @@ +[package] +name = "bracket-push" +version = "0.0.0" diff --git a/exercises/bracket-push/HINTS.md b/exercises/bracket-push/HINTS.md new file mode 100644 index 000000000..eced3f2f4 --- /dev/null +++ b/exercises/bracket-push/HINTS.md @@ -0,0 +1,6 @@ +# Bracket Push in Rust + +Reading about these Rust topics may help you implement a solution. + +- Lifetimes and Structs: https://doc.rust-lang.org/book/lifetimes.html#impl-blocks +- From trait: https://doc.rust-lang.org/std/convert/trait.From.html diff --git a/exercises/bracket-push/example.rs b/exercises/bracket-push/example.rs new file mode 100644 index 000000000..6bfde522e --- /dev/null +++ b/exercises/bracket-push/example.rs @@ -0,0 +1,79 @@ +use std::collections::HashMap; + +pub struct Brackets { + raw_brackets: Vec, + pairs: MatchingBrackets, +} + +impl<'a> From<&'a str> for Brackets { + fn from(i: &str) -> Self { + Brackets::new(String::from(i), None) + } +} + +impl Brackets { + pub fn new(s: String, pairs: Option>) -> Self { + let p = match pairs { + Some(x) => MatchingBrackets::from(x), + None => MatchingBrackets::from(vec![('[', ']'), ('{', '}'), ('(', ')')]), + }; + + Brackets { + raw_brackets: s.chars().filter(|c| p.contains(&c)).collect::>(), + pairs: p, + } + } + + pub fn are_balanced(&self) -> bool { + let mut unclosed: Vec = Vec::new(); + + for &bracket in self.raw_brackets.iter() { + if let Some(last_unclosed) = unclosed.pop() { + unclosed.extend(self.pairs.unmatched(last_unclosed, bracket)); + } else { + unclosed.push(bracket); + } + } + + unclosed.is_empty() + } +} + +pub struct MatchingBrackets { + collection: HashMap, +} + +impl From> for MatchingBrackets { + fn from(v: Vec<(char, char)>) -> Self { + MatchingBrackets { collection: v.into_iter().collect::>() } + } +} + +impl MatchingBrackets { + fn contains(&self, other: &char) -> bool { + let known = self.collection.keys().chain(self.collection.values()).collect::>(); + known.contains(&other) + } + + fn closer_for(&self, k: &char) -> Option<&char> { + self.collection.get(k) + } + + fn closed_by(&self, l: char, r: char) -> bool { + match self.closer_for(&l) { + Some(&x) => r == x, + None => false, + } + } + + fn unmatched(&self, open: char, potential_close: char) -> Vec { + let mut ret: Vec = Vec::new(); + + if !self.closed_by(open, potential_close) { + ret.push(open); + ret.push(potential_close); + } + + ret + } +} diff --git a/exercises/bracket-push/tests/bracket-push.rs b/exercises/bracket-push/tests/bracket-push.rs new file mode 100644 index 000000000..1f04471b0 --- /dev/null +++ b/exercises/bracket-push/tests/bracket-push.rs @@ -0,0 +1,82 @@ +extern crate bracket_push; + +use bracket_push::*; + +#[test] +fn paired_square_brackets() { + assert!(Brackets::from("[]").are_balanced()); +} + +#[test] +#[ignore] +fn empty_string() { + assert!(Brackets::from("").are_balanced()); +} + +#[test] +#[ignore] +fn unpaired_brackets() { + assert!(!Brackets::from("[[").are_balanced()); +} + +#[test] +#[ignore] +fn wrong_ordered_brackets() { + assert!(!Brackets::from("}{").are_balanced()); +} + +#[test] +#[ignore] +fn paired_with_whitespace() { + assert!(Brackets::from("{ }").are_balanced()); +} + +#[test] +#[ignore] +fn simple_nested_brackets() { + assert!(Brackets::from("{[]}").are_balanced()); +} + +#[test] +#[ignore] +fn several_paired_brackets() { + assert!(Brackets::from("{}[]").are_balanced()); +} + +#[test] +#[ignore] +fn paired_and_nested_brackets() { + assert!(Brackets::from("([{}({}[])])").are_balanced()); +} + +#[test] +#[ignore] +fn unopened_closing_brackets() { + assert!(!Brackets::from("{[)][]}").are_balanced()); +} + +#[test] +#[ignore] +fn unpaired_and_nested_brackets() { + assert!(!Brackets::from("([{])").are_balanced()); +} + +#[test] +#[ignore] +fn paired_and_wrong_nested_brackets() { + assert!(!Brackets::from("[({]})").are_balanced()); +} + +#[test] +#[ignore] +fn math_expression() { + assert!(Brackets::from("(((185 + 223.85) * 15) - 543)/2").are_balanced()); +} + +#[test] +#[ignore] +fn complex_latex_expression() { + let input = "\\left(\\begin{array}{cc} \\frac{1}{3} & x\\\\ \\mathrm{e}^{x} &... x^2 \ + \\end{array}\\right)"; + assert!(Brackets::from(input).are_balanced()); +} diff --git a/problems.md b/problems.md index 6fd8a35e9..2e3938446 100644 --- a/problems.md +++ b/problems.md @@ -43,6 +43,7 @@ hexadecimal | Option, zip/fold/chars, map grade-school | struct, entry api, Vec, Option tournament | enum, sorting, hashmap, structs robot-simulator | Immutability, enum +bracket-push | From trait, stack or recursion queen-attack | struct, trait (optional), Result sublist | enum, generic over type space-age | Custom Trait, From Trait, Default Trait implementation