Вправа: обчислення виразу

Давайте напишемо простий рекурсивний обчислювач арифметичних виразів.

Прикладом невеликого арифметичного виразу може бути 10 + 20, який обчислюється як 30. Ми можемо представити цей вираз у вигляді дерева:

+1020

Більшим і складнішим виразом буде (10 * 9) + ((3 - 4) * 5), що обчислюється як 85. Ми зобразимо його у вигляді набагато більшого дерева:

+**109-534

У коді ми будемо представляти дерево двома типами:

#![allow(unused)] fn main() { /// Операція для виконання над двома під-виразами. #[derive(Debug)] enum Operation { Add, Sub, Mul, Div, } /// Вираз у вигляді дерева. #[derive(Debug)] enum Expression { /// Операція над двома підвиразами. Op { op: Operation, left: Box<Expression>, right: Box<Expression> }, /// Літеральне значення Value(i64), } }

Тип Box тут є розумним вказівником і буде детально розглянутий пізніше у курсі. Вираз може бути “упаковано” за допомогою Box::new, як показано у тестах. Щоб обчислити вираз, використайте оператор розіменування (*), щоб “розпакувати” його: eval(*boxed_expr).

Деякі вирази не можуть бути обчислені і повертають помилку. Стандартний тип Result<Value, String> - це перелік, який представляє або успішне значення (Ok(Value)), або помилку (Err(String)). Ми розглянемо цей тип більш детально пізніше.

Скопіюйте та вставте код у середовище Rust і почніть реалізацію eval. Кінцевий продукт повинен пройти тести. Може бути корисно використати todo!() і змусити тести проходити один за одним. Ви також можете тимчасово оминути тест за допомогою #[ignore]:

#[test] #[ignore] fn test_value() { .. }
#![allow(unused)] fn main() { /// Операція для виконання над двома під-виразами. #[derive(Debug)] enum Operation { Add, Sub, Mul, Div, } /// Вираз у вигляді дерева. #[derive(Debug)] enum Expression { /// Операція над двома підвиразами. Op { op: Operation, left: Box<Expression>, right: Box<Expression> }, /// Літеральне значення Value(i64), } fn eval(e: Expression) -> Result<i64, String> { todo!() } #[test] fn test_value() { assert_eq!(eval(Expression::Value(19)), Ok(19)); } #[test] fn test_sum() { assert_eq!( eval(Expression::Op { op: Operation::Add, left: Box::new(Expression::Value(10)), right: Box::new(Expression::Value(20)), }), Ok(30) ); } #[test] fn test_recursion() { let term1 = Expression::Op { op: Operation::Mul, left: Box::new(Expression::Value(10)), right: Box::new(Expression::Value(9)), }; let term2 = Expression::Op { op: Operation::Mul, left: Box::new(Expression::Op { op: Operation::Sub, left: Box::new(Expression::Value(3)), right: Box::new(Expression::Value(4)), }), right: Box::new(Expression::Value(5)), }; assert_eq!( eval(Expression::Op { op: Operation::Add, left: Box::new(term1), right: Box::new(term2), }), Ok(85) ); } #[test] fn test_zeros() { assert_eq!( eval(Expression::Op { op: Operation::Add, left: Box::new(Expression::Value(0)), right: Box::new(Expression::Value(0)) }), Ok(0) ); assert_eq!( eval(Expression::Op { op: Operation::Mul, left: Box::new(Expression::Value(0)), right: Box::new(Expression::Value(0)) }), Ok(0) ); assert_eq!( eval(Expression::Op { op: Operation::Sub, left: Box::new(Expression::Value(0)), right: Box::new(Expression::Value(0)) }), Ok(0) ); } #[test] fn test_error() { assert_eq!( eval(Expression::Op { op: Operation::Div, left: Box::new(Expression::Value(99)), right: Box::new(Expression::Value(0)), }), Err(String::from("ділення на нуль")) ); } }