Ejercicio: evaluación de expresiones
Vamos a escribir un sencillo evaluador recursivo de expresiones aritméticas.
El tipo Box es un puntero inteligente y lo veremos con detalle más adelante en el curso. Una expresión puede “estar delimitada” con Box::new, tal como se observa en las pruebas. Para evaluar una expresión delimitada, usa el operador de desreferencia (*) para “eliminar la delimitación”: eval(*boxed_expr).
Algunas expresiones no se pueden evaluar y devuelven un error. El tipo estándar Result<Value, String> es una enumeración que representa un valor correcto (Ok(Value)) o un error (Err(String)). Más adelante hablaremos de este tipo en profundidad.
Copia y pega el código en el playground de Rust y comienza a implementar eval. El producto final debe superar las pruebas. Recomendamos utilizar todo!() y hacer las pruebas para superar todas las pruebas de forma individual. También puedes saltarte una prueba de forma temporal con #[ignore]:
#[test]
#[ignore]
fn test_value() { .. }
Si terminas antes, prueba a escribir una prueba que dé como resultado la división entre cero o un desbordamiento de números enteros. ¿Cómo podrías gestionarlo con Result en vez de con un pánico?
#![allow(unused)]
fn main() {
/// Operación que se puede llevar a cabo en dos subexpresiones.
#[derive(Debug)]
enum Operation {
Add,
Sub,
Mul,
Div,
}
/// Una expresión en forma de árbol.
#[derive(Debug)]
enum Expression {
/// Operación en dos subexpresiones.
Op { op: Operation, left: Box<Expression>, right: Box<Expression> },
/// Un valor literal
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_error() {
assert_eq!(
eval(Expression::Op {
op: Operation::Div,
left: Box::new(Expression::Value(99)),
right: Box::new(Expression::Value(0)),
}),
Err(String::from("división entre cero"))
);
}
}