Skip to content

Commit e669aef

Browse files
committed
wordy: rewrite example to deal with errors
Existing example was too lax with its error detection and I did not find a sensible way to bolt it on incrementally. I was unfortunately required to rewrite from scratch. The new example does have the property of being fewer lines than the previous one, if that helps...? Closes #705
1 parent ada1ad7 commit e669aef

File tree

1 file changed

+44
-56
lines changed

1 file changed

+44
-56
lines changed

exercises/wordy/example.rs

Lines changed: 44 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -1,65 +1,53 @@
1-
struct Token<'a> {
2-
value: &'a str,
1+
#[derive(PartialEq)]
2+
enum Token<'a> {
3+
Number(i32),
4+
NonNumber(&'a str),
35
}
46

5-
impl <'a> Token<'a> {
6-
fn is_valid(&self) -> bool {
7-
!self.value.is_empty() && (self.is_operand() || self.is_operator())
8-
}
9-
10-
fn is_operand(&self) -> bool {
11-
self.value.chars().all(|c| c.is_numeric() || c == '-')
12-
}
13-
14-
fn is_operator(&self) -> bool {
15-
self.value == "plus"
16-
|| self.value == "minus"
17-
|| self.value == "multiplied"
18-
|| self.value == "divided"
19-
}
7+
fn apply_op<'a, 'b>(num1: i32, words: &'a [Token<'b>]) -> Option<(i32, &'a [Token<'b>])> {
8+
let number_pos = words.iter().position(|w| match w {
9+
Token::Number(_) => true,
10+
Token::NonNumber(_) => false,
11+
})?;
12+
let (op_and_num, remainder) = words.split_at(number_pos + 1);
13+
let (op, num2) = op_and_num.split_at(number_pos);
14+
let num2 = match num2 {
15+
[Token::Number(i)] => i,
16+
_ => unreachable!("We split at a Number above, so num2 is surely a single-element slice w/ a number"),
17+
};
18+
match op {
19+
[Token::NonNumber("plus")] => Some(num1 + num2),
20+
[Token::NonNumber("minus")] => Some(num1 - num2),
21+
[Token::NonNumber("multiplied"), Token::NonNumber("by")] => Some(num1 * num2),
22+
[Token::NonNumber("divided"), Token::NonNumber("by")] => Some(num1 / num2),
23+
_ => None,
24+
}.map(|n| (n, remainder))
2025
}
2126

2227
pub fn answer(c: &str) -> Option<i32> {
23-
let mut t = tokens(c);
24-
let mut result: i32 = 0;
25-
let mut opr = "plus";
26-
27-
if t.len() <= 1 {
28-
None
29-
} else {
30-
while t.len() > 1 {
31-
result = evaluate(result, opr, operand(&t.remove(0)));
32-
opr = operator(&t.remove(0));
28+
let words = c.trim_end_matches('?').split_whitespace().map(|word| {
29+
if let Ok(i) = word.parse::<i32>() {
30+
Token::Number(i)
31+
} else {
32+
Token::NonNumber(word)
3333
}
34-
result = evaluate(result, opr, operand(&t.remove(0)));
35-
Some(result)
34+
}).collect::<Vec<_>>();
35+
if words.len() < 3 {
36+
return None;
3637
}
37-
}
38-
39-
fn evaluate(r: i32, operator: &str, operand: i32) -> i32 {
40-
match operator {
41-
"plus" => r + operand,
42-
"minus" => r - operand,
43-
"multiplied" => r * operand,
44-
"divided" => r / operand,
45-
_ => r,
38+
let mut result: i32 = match words[0..3] {
39+
[
40+
Token::NonNumber("What"),
41+
Token::NonNumber("is"),
42+
Token::Number(i),
43+
] => i,
44+
_ => return None,
45+
};
46+
let mut words = words.split_at(3).1;
47+
while !words.is_empty() {
48+
let tmp = apply_op(result, words)?;
49+
result = tmp.0;
50+
words = tmp.1;
4651
}
47-
}
48-
49-
fn operand(t: &Token) -> i32 {
50-
t.value.parse().unwrap()
51-
}
52-
53-
fn operator<'a>(t: &Token<'a>) -> &'a str {
54-
t.value
55-
}
56-
57-
fn tokens<'a>(command: &'a str) -> Vec<Token<'a>> {
58-
command
59-
.split(|c: char| c.is_whitespace() || c == '?')
60-
.map(|w| Token {
61-
value: w,
62-
})
63-
.filter(|t| t.is_valid())
64-
.collect()
52+
Some(result)
6553
}

0 commit comments

Comments
 (0)