Regex
(\d+)\s?\+\s?(\d+)
Inicia tu camino con Rust
Bienvenida al Curso Básico de Rust
Rust: un lenguaje amado por los desarrolladores
Instalando Rust en MacOS o Linux
Instalando Rust en Windows
Domina las bases de Rust
Creando un nuevo proyecto en Rust
Variables de Rust y cómo mostrarlas en pantalla
Recibiendo datos del usuario
Condicionales
Ciclo Loop
Primer proyecto: calculadora digital
Descripción del proyecto
Cargo (dependencias)
¿Qué significa unwrap?
Creando nuestra calculadora
Estructuras y funciones en Rust
Arrays y Ciclo For en Rust
Las funciones en Rust
Segundo proyecto: videojuego de texto
Descripción del proyecto: videojuego de texto
Creación y descripción del entorno
Estructuras básicas, narrativa y opciones de Rust
Interacción con el entorno y datos del usuario
Esto es solo el comienzo
Únete a la comunidad, Rustacean
No tienes acceso a esta clase
¡Continúa aprendiendo! Únete y comienza a potenciar tu carrera
No se trata de lo que quieres comprar, sino de quién quieres ser. Aprovecha el precio especial.
Antes: $249
Paga en 4 cuotas sin intereses
Termina en:
Hector Pulido
Es momento de desarrollar tu primera gran aplicación en Rust. Eres libre de idear y programar una aplicación de consola como una calculadora o un calendario. Para esto, ten presenta algunos consejos:
Explora la gran cantidad de dependencias que tiene Rust en su repositorio para encontrar las que necesites para tu app.
Algunas dependencias interesantes:
Recuerda agregar la dependencia en el archivo Cargo.toml
para su posterior descarga e importarla con use
.
use regex::Regex;
fn main() {
// ...
}
Cada dependencia tiene su propia documentación para que sepas cómo utilizarla.
Veamos algunas formas de convertir datos en Rust que te servirá de ahora en adelante.
Hay varias formas de declarar una variable del tipo String
:
let nombre = "123".to_string();
let nombre: String = "123".to_string();
let nombre = String::from("123");
let mut nombre: String = String::new();
nombre = "123".to_string();
Conviértelo a número entero con parse()
, pero teniendo en cuenta de borrar los espacios en blanco con trim()
para evitar inconvenientes y capturar los errores con unwrap()
.
let number: i32 = nombre.trim().parse().unwrap();
println!("{}", number+1);
Recuerda que el tipo de dato String
permite manipular una cadena de texto, mientras que el tipo de dato &str
contiene la referencia a un String
, pero solo contiene su valor.
Cuando le asignas a una variable un valor con dobles comillas ""
, las mismas crean un &str
, por esto utilizamos to_string()
para que retorne el tipo de dato en formato String
y poder manipularlo. Y por este mismo motivo necesitas un String
para hacer un .trim().parse()
y convertir la variable al tipo entero.
Para convertir un String
a &str
, solo agrega el &
(Ampersand) delante del nombre de la variable o con as_str()
.
let nombre: String = "123".to_string();
let nombre_plano: &str = &nombre;
let nombre: String = "123".to_string();
let nombre_plano: &str = nombre.as_str();
Muchas formas de hacer lo mismo ¿No te parece?
TIP: Por convencion, en Rust, todas las variables y nombres de funciones que declares utilizan snackcase, o sea, un
_
para separar las palabras. EJ:my_variable
omy_function
.
Rust posee una curva de aprendizaje algo más compleja cuando se trata de tipos de datos y conversiones. Como siempre, la práctica hará que puedas comprender cuándo utilizar cada tipo y ser más veloz resolviendo problemas.
Contribución creada por: Kevin Fiorentino.
Aportes 38
Preguntas 9
Regex
(\d+)\s?\+\s?(\d+)
Esta es mi solucion, con una funcion para que no se repita tanto el codigo:
use regex::Regex;
fn make_operation(reg: Regex, mut expresion: String, operation: &str) -> String {
if operation.is_empty() {
return "".to_string();
}
loop {
//Aplicar operaciones
let caps = reg.captures(expresion.as_str());
if caps.is_none() {
break;
}
let caps = caps.unwrap();
let cap_expresion = caps.get(0).unwrap().as_str();
let left_value: i32 = caps.get(1).unwrap().as_str().parse().unwrap();
let right_value: i32 = caps.get(2).unwrap().as_str().parse().unwrap();
let result = match operation {
"+" => left_value + right_value,
"-" => left_value - right_value,
"*" => left_value * right_value,
"/" => left_value / right_value,
_ => 0,
};
expresion = expresion.replace(cap_expresion, &result.to_string());
}
expresion
}
fn main() {
//Regex
let re_add = Regex::new(r"(\d+)\s?\+\s?(\d+)").unwrap();
let re_less = Regex::new(r"(\d+)\s?\-\s?(\d+)").unwrap();
let re_mult = Regex::new(r"(\d+)\s?\*\s?(\d+)").unwrap();
let re_div = Regex::new(r"(\d+)\s?/\s?(\d+)").unwrap();
//Traer datos del usuario
println!("Por favor introduce tu expresion");
let mut expresion = String::new();
std::io::stdin().read_line(&mut expresion).unwrap();
//Multiplicacion
expresion = make_operation(re_mult, expresion, "*");
//Division
expresion = make_operation(re_div, expresion, "/");
//Suma
expresion = make_operation(re_add, expresion, "+");
//Resta
expresion = make_operation(re_less, expresion, "-");
//Mostrar resultados
println!("Resultados: {}", expresion);
}
regex que incluye todas las operaciones aritméticas
(\d+)\s+?(\+|\*|/|\-)\s+?(\d+)
calculadora científica ??
Mi código repo le agregué enums para controlar de mejor forma las operaciones.
Aqui dejo mi pequeño aporte:
use regex::Regex;
fn main() {
println!("Calculadora");
// Regex
let re_add = Regex::new(r"(\d+)\s?([\+\-])\s?(\d+)").unwrap();
let re_mult = Regex::new(r"(\d+)\s?([/\*])\s?(\d+)").unwrap();
// Traer datos del usuario
println!("Ingrese una operacion: ");
let mut expression = String::new();
std::io::stdin().read_line(&mut expression).unwrap();
// Multiplicacion y Division
loop {
let caps = re_mult.captures(expression.as_str());
if caps.is_none() {
break;
}
let caps = caps.unwrap();
let cap_expression = caps.get(0).unwrap().as_str();
let left_value: i32 = caps.get(1).unwrap().as_str().parse().unwrap();
let right_value: i32 = caps.get(3).unwrap().as_str().parse().unwrap();
let sign = caps.get(2).unwrap().as_str();
if sign == "*" {
let result = left_value * right_value;
expression = expression.replace(cap_expression, &result.to_string());
} else if sign == "/" {
let result = left_value / right_value;
expression = expression.replace(cap_expression, &result.to_string());
}
}
// Suma y resta
loop {
let caps = re_add.captures(expression.as_str());
if caps.is_none() {
break;
}
let caps = caps.unwrap();
let cap_expression = caps.get(0).unwrap().as_str();
let left_value: i32 = caps.get(1).unwrap().as_str().parse().unwrap();
let right_value: i32 = caps.get(3).unwrap().as_str().parse().unwrap();
let sign = caps.get(2).unwrap().as_str();
if sign == "+" {
let result = left_value + right_value;
expression = expression.replace(cap_expression, &result.to_string());
} else if sign == "-" {
let result = left_value - right_value;
expression = expression.replace(cap_expression, &result.to_string());
}
}
// Mostrar resultado
println!("Resultado: {}", expression);
}
Quizá para este ejercicio en particular hubiese preferido usar parser combinators, pareciese que el crate nom es bastante popular en la comunidad Rust. Pero para seguir lo indicado en esta clase seguí usando expresiones regulares.
En la siguiente solución podrán apreciar un par de cosas:
r"(?P<left>\d+)\s?\+\s?(?P<right>\d+)"
. Me parece que el código queda un poco más legible de esta manera, al menos, al momento de acceder a la captura en específico parece más claro.fold
para reducir la expresión originalAcá el módulo principal:
use regex::Regex;
fn main() {
println!("expression: ");
let mut expression = String::new();
std::io::stdin().read_line(&mut expression).unwrap();
println!("Result: {}", reduce_expression(expression))
}
fn reduce_expression(mut expression: String) -> String {
let re_add = Regex::new(r"(?P<left>\d+)\s?\+\s?(?P<right>\d+)").unwrap();
let re_sub = Regex::new(r"(?P<left>\d+)\s?\-\s?(?P<right>\d+)").unwrap();
let re_mult = Regex::new(r"(?P<left>\d+)\s?\*\s?(?P<right>\d+)").unwrap();
let re_div = Regex::new(r"(?P<left>\d+)\s?/\s?(?P<right>\d+)").unwrap();
let precedence = [("/", re_div), ("*", re_mult), ("+", re_add), ("-", re_sub)];
expression = precedence
.into_iter()
.fold(expression, |acc, (operator, re)| apply(re, acc, operator));
expression
}
// apply the given operation
fn apply(re: Regex, mut expression: String, operator: &str) -> String {
loop {
let caps = re.captures(expression.as_str());
if caps.is_none() {
break;
}
let caps = caps.unwrap();
let cap_expression = caps.get(0).unwrap().as_str();
let left_value: i32 = caps.name("left").unwrap().as_str().parse().unwrap();
let right_value: i32 = caps.name("right").unwrap().as_str().parse().unwrap();
let result = match operator {
"+" => left_value + right_value,
"-" => left_value - right_value,
"*" => left_value * right_value,
"/" => left_value / right_value,
_ => 0,
};
expression = expression.replace(cap_expression, &result.to_string());
}
expression
}
Y sus correspondientes pruebas unitarias, en realidad hice una sola por aquello de la pereza.
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_apply() {
let expression: String = "12/2*4+1*12-32".to_string();
assert_eq!(reduce_expression(expression), "4");
}
}
Pienso que un pequeño cambio en la regex podría aportar más flexibilidad al usuario: en lugar de utilizar el metachar \s?
se puede utilizar \s*
para que si el usuario ingresa 10 + 20
, igual tome los valores correspondientes
I think Platzi must clarify this exercise because the teacher’s implementation is wrong.
Aquí dejo mi aporte, todo esta estructurado en un main, en próximas sesiones lo hare mediante funciones. Le hice una prueba intensiva de escritorio. Les dejo el link del github.
Divison
let _re_div=Regex::new(r"(\d+)\s?/\s?(\d+)").unwrap();
les dejo mi codigo fuente,
github
agrupé multiplicacion y division porque sino la que iria antes en el código es la que tendria preferencia en vez de ir en orden de lectura (propiedad asociativa)
creo que agrupar suma y resta no seria necesario porque son conmutativas
use regex::Regex;
fn main() {
let re_add_sub = Regex::new("(\\d+)\\s?([\\+\\-])\\s?(\\d+)").unwrap();
let re_mult_div = Regex::new("(\\d+)\\s?([\\*\\/])\\s?(\\d+)").unwrap();
println!("Introduzca la operación: ");
let mut expression = String::new();
std::io::stdin().read_line(&mut expression).unwrap();
//multiplication and division
loop{
let caps = re_mult_div.captures(&expression);
if caps.is_none(){
break;
}
let caps = caps.unwrap();
// let caps_expression = caps.get(0).unwrap().as_str();
let caps_num1 : i32 = caps.get(1).unwrap().as_str().parse::<i32>().unwrap();
let caps_num2 : i32 = caps.get(3).unwrap().as_str().parse::<i32>().unwrap();
let operator = &caps[2];
let result = match operator {
"*" => caps_num1 * caps_num2,
"/" => caps_num1 / caps_num2,
_ => {
println!("Operador no válido: {}", operator);
break;
}
};
expression = expression.replacen(&caps[0], &result.to_string(), 1);
println!("La suma es: {}", result);
}
//add and sub
loop{
let caps = re_add_sub.captures(&expression);
if caps.is_none(){
break;
}
let caps = caps.unwrap();
// let caps_expression = caps.get(0).unwrap().as_str();
let caps_num1 : i32 = caps.get(1).unwrap().as_str().parse::<i32>().unwrap();
let caps_num2 : i32 = caps.get(3).unwrap().as_str().parse::<i32>().unwrap();
let operator = &caps[2];
let result = match operator {
"+" => caps_num1 + caps_num2,
"-" => caps_num1 - caps_num2,
_ => {
println!("Operador no válido: {}", operator);
break;
}
};
expression = expression.replacen(&caps[0], &result.to_string(), 1);
println!("La suma es: {}", result);
}
}
Mi Solución cambie la expresión regular por 2 la primera evalua multiplicación y división y la segunda suma y resta.
Luego saca el simbolo para saber que operación realizar.
use regex::Regex;
use std::io::stdin;
fn operaciones(re: Regex, mut expression: String) -> String {
loop {
//multipliction
let caps = re.captures(expression.as_str());
if caps.is_none() {
break;
}
let caps = caps.unwrap();
let cap_expression = caps.get(0).unwrap().as_str();
let left_value: i32 = caps.get(1).unwrap().as_str().parse().unwrap();
let symbol: &str = caps.get(2).unwrap().as_str();
let right_value: i32 = caps.get(3).unwrap().as_str().parse().unwrap();
let operation = match symbol {
"*" => left_value * right_value,
"/" => left_value / right_value,
"-" => left_value - right_value,
"+" => left_value + right_value,
&_ => todo!(),
};
expression = expression.replace(cap_expression, &operation.to_string());
}
return expression;
}
fn main() {
//Regex
let re_expresion_first = Regex::new(r"(\d+)\s?(\*|\/)\s?(\d+)").unwrap();
let re_expresion_second = Regex::new(r"(\d+)\s?(\+|\-)\s?(\d+)").unwrap();
// traer datos de usuario
println!("Por favor introduce tu expresión: ");
let mut expression = String::new();
stdin().read_line(&mut expression).unwrap();
//aplicar operaciones
expression = operaciones(re_expresion_first, expression);
expression = operaciones(re_expresion_second, expression);
// mostrar resultados
println!("Resultado: {}", expression);
}
Muy buena la clase, lo que si puedo recomendar es que deben explicar de manera mas detallada para los principiantes que el loop simpre va a iterar sobre las dos primeros operandos de la variable expression. Es decir, si yo como usuario introduzco A+B+C la primera vez que itere el programa este me va a reconocer solo A+B, luego reemplaza A+B por el resultado de esta suma en la variable expression. En la segunda iteracion tenemos que expression = resultadoA+B + C, y esta seria la ultima iteracion del programa ya que en expression no conseguiria mas regex de la forma D + D.
Pero me parece que tiene sus fallas
use regex::Regex;
fn main() {
println!("Hola Platzi");
// Regex
let re_add = Regex::new(r"(\d+)\s?\+\s?(\d+)").unwrap();
let re_mult = Regex::new(r"(\d+)\s?\*\s?(\d+)").unwrap();
let re_resta = Regex::new(r"(\d+)\s?\-\s?(\d+)").unwrap();
let re_div = Regex::new(r"(\d+)\s?\/\s?(\d+)").unwrap();
// Traer datos del usuario
println!("Por favor introduce tu expresion: ");
let mut expression = String::new();
std::io::stdin().read_line(&mut expression).unwrap();
// multiplicacion
loop {
let caps = re_mult.captures(expression.as_str());
if caps.is_none() {
break;
}
let caps = caps.unwrap();
let cap_expression = caps.get(0).unwrap().as_str();
let left_value : i32 = caps.get(1).unwrap().as_str().parse().unwrap();
let right_value : i32 = caps.get(2).unwrap().as_str().parse().unwrap();
let mult = left_value * right_value;
expression = expression.replace(cap_expression, &mult.to_string());
}
//suma
loop {
let caps = re_add.captures(expression.as_str());
if caps.is_none() {
break;
}
let caps = caps.unwrap();
let cap_expression = caps.get(0).unwrap().as_str();
let left_value : i32 = caps.get(1).unwrap().as_str().parse().unwrap();
let right_value : i32 = caps.get(2).unwrap().as_str().parse().unwrap();
let addition = left_value + right_value;
expression = expression.replace(cap_expression, &addition.to_string());
}
// división
loop {
let caps = re_div.captures(expression.as_str());
if caps.is_none() {
break;
}
let caps = caps.unwrap();
let cap_expression = caps.get(0).unwrap().as_str();
let left_value : i32 = caps.get(1).unwrap().as_str().parse().unwrap();
let right_value : i32 = caps.get(2).unwrap().as_str().parse().unwrap();
let div = left_value / right_value;
expression = expression.replace(cap_expression, &div.to_string());
}
//resta
loop {
let caps = re_resta.captures(expression.as_str());
if caps.is_none() {
break;
}
let caps = caps.unwrap();
let cap_expression = caps.get(0).unwrap().as_str();
let left_value : i32 = caps.get(1).unwrap().as_str().parse().unwrap();
let right_value : i32 = caps.get(2).unwrap().as_str().parse().unwrap();
let resta = left_value - right_value;
expression = expression.replace(cap_expression, &resta.to_string());
}
println!("Resultado: {}", expression);
}
Mi solución, pero espero mejorarla con las funciones.
use::regex::Regex;
fn main() {
// Regex for matching the input
let re_div = Regex::new(r"(\d+)\s?/\s?(\d+)").unwrap();
let re_mul = Regex::new(r"(\d+)\s?\*\s?(\d+)").unwrap();
let re_add = Regex::new(r"(\d+)\s?\+\s?(\d+)").unwrap();
let re_sub = Regex::new(r"(\d+)\s?\-\s?(\d+)").unwrap();
// User input
println!("Enter an operation to perform: ");
let mut expression = String::new();
std::io::stdin().read_line(&mut expression).unwrap();
// Division
loop {
// Calculate operations
let caps = re_div.captures(&expression);
if caps.is_none() {
break;
}
let caps = caps.unwrap();
let cap_expression = caps.get(0).unwrap().as_str();
let left_value = caps.get(1).unwrap().as_str().parse::<i32>().unwrap();
let right_value = caps.get(2).unwrap().as_str().parse::<i32>().unwrap();
let result = left_value / right_value;
expression = expression.replace(cap_expression, &result.to_string());
}
// Multiplication
loop {
// Calculate operations
let caps = re_mul.captures(&expression);
if caps.is_none() {
break;
}
let caps = caps.unwrap();
let cap_expression = caps.get(0).unwrap().as_str();
let left_value = caps.get(1).unwrap().as_str().parse::<i32>().unwrap();
let right_value = caps.get(2).unwrap().as_str().parse::<i32>().unwrap();
let result = left_value * right_value;
expression = expression.replace(cap_expression, &result.to_string());
}
// Addition
loop {
// Calculate operations
let caps = re_add.captures(&expression);
if caps.is_none() {
break;
}
let caps = caps.unwrap();
let cap_expression = caps.get(0).unwrap().as_str();
let left_value = caps.get(1).unwrap().as_str().parse::<i32>().unwrap();
let right_value = caps.get(2).unwrap().as_str().parse::<i32>().unwrap();
let result = left_value + right_value;
expression = expression.replace(cap_expression, &result.to_string());
}
// Subtraction
loop {
// Calculate operations
let caps = re_sub.captures(&expression);
if caps.is_none() {
break;
}
let caps = caps.unwrap();
let cap_expression = caps.get(0).unwrap().as_str();
let left_value = caps.get(1).unwrap().as_str().parse::<i32>().unwrap();
let right_value = caps.get(2).unwrap().as_str().parse::<i32>().unwrap();
let result = left_value - right_value;
expression = expression.replace(cap_expression, &result.to_string());
}
println!("Result: {}", expression);
}```
esta clase me parecio bastante interesante y me hizo pensar en como funciona los lenguajes interpretados, por ejemplo python3. probablemente no funcionan asi pero pues imaginen crear un lenguaje de programacion en base a rust.
les comparto mi aporte:
use regex::Regex;
fn main() {
// regex
let re_add: Regex = Regex::new(r"(\d+)\s?([+-])\s?(\d+)").unwrap();
let re_mul: Regex = Regex::new(r"(\d+)\s?([*/])\s?(\d+)").unwrap();
// user input
let mut expression = String::new();
println!("Enter an expression: ");
std::io::stdin().read_line(&mut expression).unwrap();
// validate input
if !re_add.is_match(&expression) && !re_mul.is_match(&expression) {
println!("Invalid expression");
return;
}
// multiplication and divition
loop {
let captures = re_mul.captures(expression.as_str());
if captures.is_none() {
break;
}
let captures = captures.unwrap();
let cap_expression = captures.get(0).unwrap().as_str();
let left_value = captures.get(1).unwrap().as_str().parse::<i32>().unwrap();
let operator = captures.get(2).unwrap().as_str();
let right_value = captures.get(3).unwrap().as_str().parse::<i32>().unwrap();
let mut result_mul = 0;
if operator == "*" {
result_mul = left_value * right_value;
} else if operator == "/" {
result_mul = left_value / right_value;
}
expression = expression.replace(cap_expression, &result_mul.to_string());
}
// addition and subtraction
loop {
let captures = re_add.captures(expression.as_str());
if captures.is_none() {
break;
}
let captures = captures.unwrap();
let cap_expression = captures.get(0).unwrap().as_str();
let left_value = captures.get(1).unwrap().as_str().parse::<i32>().unwrap();
let operator = captures.get(2).unwrap().as_str();
let right_value = captures.get(3).unwrap().as_str().parse::<i32>().unwrap();
let mut result_sum = 0;
if operator == "+" {
result_sum = left_value + right_value;
} else if operator == "-" {
result_sum = left_value - right_value;
}
expression = expression.replace(cap_expression, &result_sum.to_string());
}
// print result
println!("Result: {}", expression);
}
Para tener en cuenta los números negativos en las suma u otra operación al inicio de la operación se debe agregar -?
ejemplo (-?\d+)\s?+\s?(\d+)
Usando match y una función para evitar repetir código
use regex::{Captures, Regex};
fn main() {
// Traer datos del usuario
println!("Por favor introduce tu expresion: ");
let mut expresion = String::new();
std::io::stdin().read_line(&mut expresion).unwrap();
expresion = make_math(expresion, "*".to_string());
expresion = make_math(expresion, "/".to_string());
expresion = make_math(expresion, "+".to_string());
expresion = make_math(expresion, "-".to_string());
// Mostrar resultado
println!("Resultado: {}", expresion)
}
fn make_math(expresion: String, operator: String) -> String {
// Regex
let re_mul = Regex::new(r"(\d+)\s?\*\s?(\d+)").unwrap();
let re_div = Regex::new(r"(\d+)\s?/\s?(\d+)").unwrap();
let re_add = Regex::new(r"(\d+)\s?\+\s?(\d+)").unwrap();
let re_minus = Regex::new(r"(\d+)\s?\-\s?(\d+)").unwrap();
let mut expresion = expresion;
loop {
// Aplicar operaciones
let caps = match operator.as_str() {
"*" => re_mul.captures(expresion.as_str()),
"/" => re_div.captures(expresion.as_str()),
"+" => re_add.captures(expresion.as_str()),
"-" => re_minus.captures(expresion.as_str()),
_ => panic!(),
};
// let caps = caps.captures(expresion.as_str());
if caps.is_none() {
break;
}
let caps = caps.unwrap();
let cap_expression = caps.get(0).unwrap().as_str();
let left_value: i32 = caps.get(1).unwrap().as_str().parse().unwrap();
let right_value: i32 = caps.get(2).unwrap().as_str().parse().unwrap();
let result = match operator.as_str() {
"*" => left_value * right_value,
"/" => left_value / right_value,
"+" => left_value + right_value,
"-" => left_value - right_value,
_ => panic!(),
};
expresion = expresion.replace(cap_expression, &(result).to_string());
}
return expresion;
}
Llevo mucho tiempo programando en Python y decidí que mi primer lenguaje, para aprender, bajo nivel iba a ser Rust. No ha sido fácil entender todo y menos tener tratar con los tipos y cosas parecidas al operador &. Pero, aquí está mi ejercicio. Traté de expandirlo un poco más y adicional a las 4 operaciones básicas, también soporta potenciación y agrupaciones con paréntesis.
use regex::Regex;
fn math_op(expression: &Regex, mut operation: String, operator: &str) -> String {
loop {
let caps = expression.captures(operation.as_str());
if caps.is_none() {
break;
}
let caps = caps.unwrap();
let cap_expression = caps.get(0).unwrap().as_str();
let cap_num_1: f64 = caps.get(1).unwrap().as_str().parse().unwrap();
let cap_num_2: f64 = caps.get(2).unwrap().as_str().parse().unwrap();
let result = match operator {
"+" => cap_num_1 + cap_num_2,
"-" => cap_num_1 - cap_num_2,
"*" => cap_num_1 * cap_num_2,
"/" => cap_num_1 / cap_num_2,
"^" => if cap_num_2 < 0.0 {cap_num_1/cap_num_2} else {cap_num_1.powi(cap_num_2 as i32)},
_ => 0.0,
};
operation = operation.replace(cap_expression, &result.to_string());
}
operation
}
fn main() {
// Regex
let re_add = Regex::new(r"(\d+)\s?\+\s?(\d+)").unwrap();
let re_sub = Regex::new(r"(\d+)\s?\-\s?(\d+)").unwrap();
let re_mult = Regex::new(r"(\d+)\s?\*\s?(\d+)").unwrap();
let re_div = Regex::new(r"(\d+)\s?/\s?(\d+)").unwrap();
let power = Regex::new(r"(\d+)\s?\^\s?(-?\d+)").unwrap();
let parentesis = Regex::new(r"\(([^()]+)\)").unwrap();
// Traer datos del usuario
println!("Por favor introduce tu expresion: ");
let mut expression = String::new();
std::io::stdin().read_line(&mut expression).unwrap();
loop {
let cap_par = parentesis.captures(expression.as_str());
if cap_par.is_none() {
break;
}
let cap_par = cap_par.unwrap();
let capture = cap_par.get(0).unwrap().as_str();
println!("Formula entre parentesis {}", capture);
let mut res = math_op(&power, String::from(capture).clone(), "^");
res = math_op(&re_mult,String::from(res).clone(), "*");
res = math_op(&re_div, String::from(res).clone(), "/");
res = math_op(&re_add, String::from(res).clone(), "+");
res = math_op(&re_sub, String::from(res).clone(), "-");
res = res.replace("(", "");
res = res.replace(")", "");
println!("{:?}", res);
expression = expression.replace(capture, &res.to_string());
}
expression = math_op(&power, expression.clone(), "^");
expression = math_op(&re_mult, expression.clone(), "*");
expression = math_op(&re_div, expression.clone(), "/");
expression = math_op(&re_add, expression.clone(), "+");
expression = math_op(&re_sub, expression.clone(), "-");
println!("Resultado: {}", expression)
// Aplicar Operaciones
}
Por acá dejo mi solución, aún no hago funciones aparte, espero esa parte del curso:
use regex::Regex;
fn main() {
//regex
let re_mul = Regex::new(r"(\d+)\s?\*\s?(\d+)").unwrap();
let re_div = Regex::new(r"(\d+)\s?/\s?(\d+)").unwrap();
let re_add = Regex::new(r"(\d+)\s?\+\s?(\d+)").unwrap();
let re_sub = Regex::new(r"(\d+)\s?\-\s?(\d+)").unwrap();
//get user input
println!("Please, enter your math expression: ");
let mut expression = String::new();
std::io::stdin().read_line(&mut expression).unwrap();
//multiplication
loop {
//validate math operations
let caps = re_mul.captures(expression.as_str());
if caps.is_none() {
break;
}
let caps = caps.unwrap();
let cap_expression = caps.get(0).unwrap().as_str();
let left_value: i32 = caps.get(1).unwrap().as_str().parse().unwrap();
let right_value: i32 = caps.get(2).unwrap().as_str().parse().unwrap();
let mul = left_value * right_value;
expression = expression.replace(cap_expression, &mul.to_string());
}
//division
loop {
//validate math operations
let caps = re_div.captures(expression.as_str());
if caps.is_none() {
break;
}
let caps = caps.unwrap();
let cap_expression = caps.get(0).unwrap().as_str();
let left_value: i32 = caps.get(1).unwrap().as_str().parse().unwrap();
let right_value: i32 = caps.get(2).unwrap().as_str().parse().unwrap();
let div = left_value / right_value;
expression = expression.replace(cap_expression, &div.to_string());
}
//addition
loop {
//validate math operations
let caps = re_add.captures(expression.as_str());
if caps.is_none() {
break;
}
let caps = caps.unwrap();
let cap_expression = caps.get(0).unwrap().as_str();
let left_value: i32 = caps.get(1).unwrap().as_str().parse().unwrap();
let right_value: i32 = caps.get(2).unwrap().as_str().parse().unwrap();
let addition = left_value + right_value;
expression = expression.replace(cap_expression, &addition.to_string());
}
//subtraction
loop {
//validate math operations
let caps = re_sub.captures(expression.as_str());
if caps.is_none() {
break;
}
let caps = caps.unwrap();
let cap_expression = caps.get(0).unwrap().as_str();
let left_value: i32 = caps.get(1).unwrap().as_str().parse().unwrap();
let right_value: i32 = caps.get(2).unwrap().as_str().parse().unwrap();
let sub = left_value - right_value;
expression = expression.replace(cap_expression, &sub.to_string());
}
//print result
println!("Result: {}", expression);
}
Hice la calculadora usando otras funciones para separar las responsabilidades, además, calcula resta, multiplicación y división. Incluso se puede usar parentesis, pero hay bugs porque falta aplicar la norma de operar de izquierda a derecha.
Posdata: En mi código se puede usar varios espacios porque al inicio los filtra.
Posdata: Hice uso de struct para estar tipando los objetos que estuve usando, solo aplique lo que se de Ts.
Este es el repositorio.
use std::io::{stdin};
use regex::{Regex, Captures};
struct RegexOperators {
sum: Regex,
substract: Regex,
divide: Regex,
multiply: Regex,
}
struct Expression {
equation: String,
parenthesis_1: String,
parenthesis_2: String,
variable_1: i16,
variable_2: i16,
symbol: String
}
pub fn exercise_calculator() {
println!("Digit the equation.");
let operators = RegexOperators {
sum: Regex::new(r"(\(?)(\d+)(\+)(\d+)(\)?)").unwrap(),
substract: Regex::new(r"(\(?)(\d+)(\-)(\d+)(\)?)").unwrap(),
divide: Regex::new(r"(\(?)(\d+)(/)(\d+)(\)?)").unwrap(),
multiply: Regex::new(r"(\(?)(\d+)(\*)(\d+)(\)?)").unwrap(),
};
let mut expression: String = String::new();
stdin().read_line(&mut expression).unwrap();
let expression_no_spaces: String = expression.replace(" ", "");
let mut answer: String = calculator(expression_no_spaces, operators.divide);
answer = calculator(answer, operators.multiply);
answer = calculator(answer, operators.sum);
answer = calculator(answer, operators.substract);
println!("The answer is: {answer}");
}
fn destructure_caps (caps: Captures) -> Expression{
let equation = Expression {
parenthesis_1: caps.get(1).unwrap().as_str().to_string(),
parenthesis_2: caps.get(5).unwrap().as_str().to_string(),
symbol: caps.get(3).unwrap().as_str().to_string(),
variable_1: caps.get(2).unwrap().as_str().parse().unwrap(),
variable_2: caps.get(4).unwrap().as_str().parse().unwrap(),
equation: caps.get(0).unwrap().as_str().to_string(),
};
return equation;
}
fn calculate (variable_1: i16, variable_2: i16, symbol: &str) -> i16{
let result: i16 = match symbol {
"+" => variable_1 + variable_2,
"-" => variable_1 - variable_2,
"*" => variable_1 * variable_2,
"/" => if variable_2 == 0 {
0
} else {
variable_1 / variable_2
},
_ => 0,
};
return result;
}
fn get_operation (equation: &Expression, result: i16) -> String {
let new_operation: String;
if equation.parenthesis_1 == "(" && equation.parenthesis_2 == ")"{
new_operation = result.to_string();
} else {
new_operation = format!("{}{}{}", equation.parenthesis_1, &result.to_string(), equation.parenthesis_2);
}
return new_operation;
}
fn calculator (expression: String, operator: Regex) -> String {
let mut operation: String = expression.clone();
loop {
let caps: Option<Captures> = operator.captures(&operation);
if caps.is_none() {
break;
}
let caps_unwraped: Captures = caps.unwrap();
let equation: Expression = destructure_caps(caps_unwraped);
let result: i16 = calculate(equation.variable_1, equation.variable_2, equation.symbol.as_str());
let new_operation: String = get_operation(&equation, result);
operation = operation.replace(equation.equation.as_str(), &new_operation);
}
return operation;
}
Esta es mi solucion para obtener los valores, pensando en un algo mas legible 😄
let caps = re_add.captures(&expresion).unwrap();
let left_value : i32 = caps[1].parse().unwrap();
let right_value : i32 = caps[2].parse().unwrap();
println!("{:?} izq: {}, der: {}", caps, left_value, right_value);
Use una funcion para cumplir con el principio DRY (Don’t Repeat Yourself), usando ademas un switch (se llama match en Rust) y un enum para controlar el tipo de operacion a realizar
Ademas, aunque no se Regex aun, logre hacer una expresion que pudiera utilizar decimales, pues al haber divisiones es siempre posible que hayan resultados con decimales.
Creo que se puede mejorar el regex y se que posiblemente haya algun que otro error de logica, como en el orden de operaciones en el loop, asi que bienvenidas las recomendaciones en todo sentido
use regex::Regex;
enum TipoOperacion {
SUMA,
RESTA,
MULTIPLICACION,
DIVISION,
}
fn loop_operaciones(string_regex: Regex, mut expresion: String, tipo_operacion: TipoOperacion) -> String {
loop {
// Aplicar Operaciones
let caps = string_regex.captures(expresion.as_str());
if caps.is_none() {
break;
}
let caps = caps.unwrap();
let cap_expresion = caps.get(0).unwrap().as_str();
// Aqui primero se accede a la posicion 1, se pasa a str, luego se parsea a f32 y un ultimo unwrap
let left_value: f32 = caps.get(1).unwrap().as_str().parse().unwrap();
let right_value: f32 = caps.get(2).unwrap().as_str().parse().unwrap();
println!("{:?} izq: {} der: {}", caps, left_value, right_value); // :? dentro de los corchetes hace que de mas informacion
println!("{}", expresion);
let resultado: f32;
match tipo_operacion {
TipoOperacion::SUMA => resultado = left_value + right_value,
TipoOperacion::RESTA => resultado = left_value - right_value,
TipoOperacion::MULTIPLICACION => resultado = left_value * right_value,
TipoOperacion::DIVISION => resultado = left_value / right_value,
}
expresion = expresion.replace(cap_expresion, &resultado.to_string()); // amperson al principio transforma un String a un str
}
return expresion;
}
fn main() {
// Regex
// Esta es la regex que me funciono para este caso y que desarrolle sobre la marcha, buscar mejor solucion
let re_suma = Regex::new(r"(\d+\.?\d*)\s*\+\s*(\d+\.?\d*)").unwrap();
let re_mult = Regex::new(r"(\d+\.?\d*)\s*\*\s*(\d+\.?\d*)").unwrap();
let re_resta = Regex::new(r"(\d+\.?\d*)\s*\-\s*(\d+\.?\d*)").unwrap();
let re_division = Regex::new(r"(\d+\.?\d*)\s*/\s*(\d+\.?\d*)").unwrap();
// (\d+) \s? \+ \s? (\d+)
/*
Regex dividida en 5 partes
Esta regex va a buscar primero un numero de uno o mas digitos
Luego va a buscar un espacio que seria opcional
Luego un signo de mas
Luego otro espacio opcional
Y por ultimo el otro numero
Los espacios opcionales son por si el usuario ingresa por ejemplo 1 +2 o 2+ 34 o 3 + 6
Y pues tambien sirve sin los espacios opcionales 2+5
*/
// Traer datos del usuario
let mut expresion = String::new();
println!("Ingrese la expresion: ");
std::io::stdin().read_line(&mut expresion).unwrap();
expresion = loop_operaciones(re_mult, expresion, TipoOperacion::MULTIPLICACION);
expresion = loop_operaciones(re_division, expresion, TipoOperacion::DIVISION);
expresion = loop_operaciones(re_suma, expresion, TipoOperacion::SUMA);
expresion = loop_operaciones(re_resta, expresion, TipoOperacion::RESTA);
// Mostrar resultado
println!("Resultado: {}", expresion);
}
Asi se desarrolla en la consola:
Ingrese la expresion:
3*5/6+2-7+3*2+6/7
Captures({0: Some("3*5"), 1: Some("3"), 2: Some("5")}) izq: 3 der: 5
3*5/6+2-7+3*2+6/7
Captures({0: Some("3*2"), 1: Some("3"), 2: Some("2")}) izq: 3 der: 2
15/6+2-7+3*2+6/7
Captures({0: Some("15/6"), 1: Some("15"), 2: Some("6")}) izq: 15 der: 6
15/6+2-7+6+6/7
Captures({0: Some("6/7"), 1: Some("6"), 2: Some("7")}) izq: 6 der: 7
2.5+2-7+6+6/7
Captures({0: Some("2.5+2"), 1: Some("2.5"), 2: Some("2")}) izq: 2.5 der: 2
2.5+2-7+6+0.85714287
Captures({0: Some("7+6"), 1: Some("7"), 2: Some("6")}) izq: 7 der: 6
4.5-7+6+0.85714287
Captures({0: Some("13+0.85714287"), 1: Some("13"), 2: Some("0.85714287")}) izq: 13 der: 0.85714287
4.5-13+0.85714287
Captures({0: Some("4.5-13.857142"), 1: Some("4.5"), 2: Some("13.857142")}) izq: 4.5 der: 13.857142
4.5-13.857142
Resultado: -9.357142
técnicamente es una calculadora pero no cientifica 😛
Reto 4 Calculadora con */±
https://github.com/jeigar2/curso-rust/tree/04-Calculadora-v1.0
aca dejo mi solucion
https://github.com/alealvb/rust-calculator
Comparto mi solución:
<code>
use regex::Regex;
fn main() {
// Regex
let re_add = Regex::new(r"(-?\d+)\s?([\+|\-])\s?(-?\d+)").unwrap();
let re_prod = Regex::new(r"(-?\d+)\s?([\*|/])\s?(-?\d+)").unwrap();
// Tomar datos de usuario
println!("Ingresar expresión: ");
let mut expression = String::new();
std::io::stdin().read_line(&mut expression).unwrap();
// Interpretar expresión y operar
expression = read(expression, re_prod);
expression = read(expression, re_add);
// Mostrar resultados
println!("Resultado: {}", expression)
}
fn read(mut exp: String, re: Regex) -> String {
loop {
let caps = re.captures(exp.as_str());
if caps.is_none() {
break;
}
let caps = caps.unwrap();
let caps_exp = caps.get(0).unwrap().as_str();
let left_value: i32 = caps.get(1).unwrap().as_str().parse().unwrap();
let operator: &str = caps.get(2).unwrap().as_str();
let right_value: i32 = caps.get(3).unwrap().as_str().parse().unwrap();
let res: i32 = operate(left_value, right_value, operator);
exp = exp.replace(caps_exp, &res.to_string());
}
return exp;
}
fn operate(a: i32, b: i32, op: &str) -> i32 {
match op {
"+" => a + b,
"-" => a - b,
"*" => a * b,
"/" => a / b,
_ => 0,
}
}
Esta es mi solución al proyecto. Utilizo una sola regex para todas las operaciones matemáticas introduciendo un nuevo wildcard. Además agrego la opción de entender comandos, “quit” para salir, “help” para obtener ayuda. Apreciaría sus comentarios.
use regex::Regex;
use std::io::{stdin};
fn main() {
println!("Bienvenido o bienvenida.");
println!("Introduzca una expresion matematica o pida ayuda con 'help'.");
loop {
// Regex definition
let re_math = Regex::new(r"(\d+)\s?(\+|-|\*|/)\s?(\d+)").unwrap();
let re_commands = Regex::new(r"(quit|help)").unwrap();
// Operation Capture
let mut exp = String::new();
stdin().read_line(&mut exp).unwrap();
let exp = exp.trim();
// Regex Commands
let commands_caps = re_commands.captures(exp);
if commands_caps.is_some() {
let command = commands_caps.unwrap().get(1).unwrap().as_str();
match command {
"quit" => break,
"help" => println!("Introduzca una expresión matemática"),
_ => println!("Comando no reconocido"),
}
continue
}
// Regex Match
let operation_caps = re_math.captures(exp);
if operation_caps.is_some() {
let caps = operation_caps.unwrap();
let elem1: i32 = caps.get(1).unwrap().as_str().parse().unwrap();
let elem2: i32 = caps.get(3).unwrap().as_str().parse().unwrap();
let op: &str = caps.get(2).unwrap().as_str();
match op {
"+" => println!("=> {}", elem1 + elem2),
"-" => println!("=> {}", elem1 - elem2),
"*" => println!("=> {}", elem1 * elem2),
"/" => println!("=> {}", elem1 / elem2),
_ => println!("Operación no reconocida"),
}
continue
}
println!("Expresión no reconocida. Escriba 'help' para obtener ayuda.");
}
}
Better TOML, plugin VScode.
¿Quieres ver más aportes, preguntas y respuestas de la comunidad?