|
1 |
| -struct Token<'a> { |
2 |
| - value: &'a str, |
| 1 | +#[derive(PartialEq)] |
| 2 | +enum Token<'a> { |
| 3 | + Number(i32), |
| 4 | + NonNumber(&'a str), |
3 | 5 | }
|
4 | 6 |
|
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)) |
20 | 25 | }
|
21 | 26 |
|
22 | 27 | 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) |
33 | 33 | }
|
34 |
| - result = evaluate(result, opr, operand(&t.remove(0))); |
35 |
| - Some(result) |
| 34 | + }).collect::<Vec<_>>(); |
| 35 | + if words.len() < 3 { |
| 36 | + return None; |
36 | 37 | }
|
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; |
46 | 51 | }
|
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) |
65 | 53 | }
|
0 commit comments