diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 0000000..75a91b5 Binary files /dev/null and b/.DS_Store differ diff --git a/dam1_calculadora/angel_calculadora_alumnos.py b/dam1_calculadora/angel_calculadora_alumnos.py new file mode 100644 index 0000000..dc7de54 --- /dev/null +++ b/dam1_calculadora/angel_calculadora_alumnos.py @@ -0,0 +1,420 @@ +# TODO: ATENCIÓN!!! +# 1. Importar los paquetes necesarios +# 2. Usar la función mostrar_error para imprimir los errores por consola. +# 3. Los comentarios TODO os ayudan a resolver cada apartado de esta prueba. +# 4. CADA función SOLO puede tener una instrucción return. +# 5. En test_calculadora_alumnos.py crear el test unitario para la función es_resultado_negativo y hacer que todos los test unitarios se cumplan. + +import os +import time +import math + +# Mensajes de error predefinidos +MENSAJES_ERROR = ( + "Problemas al intentar limpiar la pantalla {error}", + "Error al configurar los decimales. Formato: decimales .", + "Entrada no válida. Ingrese número, operador, 'resultado', 'cancelar' o para finalizar el cálculo.", + "Error: Introduzca un operador antes de otro número.", + "Comando no reconocido. Escriba 'lista' para ver las operaciones disponibles.", + "Error: no es posible la división por 0! Introduzca otro valor diferente a 0...", + "Se produjo un error: {error}" +) + +# Operadores soportados por la calculadora +OPERADORES = ('+', '-', 'x', '*','/', ':', '**', 'exp') +# TODO: Ayuda: incluye en esta constante los símbolos que reconocerá la aplicación para el cálculo de las operaciones: +# '+' para la suma. +# '-' para la resta. +# 'x' o '*' para la multiplicación. +# '/' o ':' para la división. +# '**' o 'exp' para la potencia. + + + +def limpiar_pantalla(): + """ + Limpia la consola según el sistema operativo. + """ + # TODO: El desarrollo de esta función está incompleto y con errores. + # Otra forma de expresar la misma instrucción sería la siguiente: + if os.name == "possix": + os.system("clear") + else: + os.system("cls") + + + +def pausa(tiempo = 2): + """ + Pausa la ejecución del programa hasta que se pulse ENTER... "\nPresione ENTER para continuar..." + """ + # TODO: Desarrollar esta función según su documentación. + time.sleep(tiempo) + + +def mostrar_error(indice_error: int, msj_error = None): + """ + Muestra un mensaje de error en la consola. + + Args: + indice_error (int): Índice del mensaje de error en MENSAJES_ERROR. + msj_error (str, opcional): Texto adicional para personalizar el mensaje de error. + """ + # TODO: + # 1. Corrige el error que existe a la hora de acceder a los mensajes de error que están en la constante + # MENSAJES_ERROR. A los elementos de una tupla se accede igual que a los caracteres de una + # cadena de caracteres. + # 2. Completa el código de esta función para que controle específicamente las excepciones IndexError y + # muestre el mensaje: "\n*ERROR* Mensaje de error no definido.\n" + # 3. También se pide que se controle cualquier otra excepción que se pueda producir y muestre el mensaje: + # "\n*ERROR* Problemas al mostrar error!\n{e}\n" + # 4. En esta función los mensajes de error deben mostrarse con print. + if msj_error != None: + print(f"\n*ERROR* {MENSAJES_ERROR.format(error = msj_error)}\n") + else: + print(f"\n*ERROR* {MENSAJES_ERROR}\n") + + +def sumar(resultado, num1, num2): + # TODO: Realizar el desarrollo completo, incluida la documentación... recibe 2 números float y retorna la suma de ambos. + resultado = num1 + num2 + return float(range(resultado,2)) + + + +def restar(resultado, num1, num2 ): + # TODO: Realizar el desarrollo completo, incluida la documentación... recibe 2 números float y retorna la resta de ambos. + resultado = num1 - num2 + return float(range(resultado,2)) + + + +def es_resultado_negativo(num1: float, num2: float) -> bool: + """Determina si el resultado de una operación entre num1 y num2 debe ser negativo.""" + # TODO: Realizar el desarrollo completo de esta función teniendo en cuenta la documentación y completándola también. + # Debe pasar las pruebas unitarias. + # Se trata de una función que os puede venir bien para utilizarla tanto en la función multiplicar, como en dividir. + # Ya que va a determinar si la multiplicación o división entre dos números debería ser de signo negativo o no. + if num2 :print + + + +def multiplicar(num1, num2): + """ + Realiza la multiplicación ENTERA de dos números usando solo sumas y restas. + + Args: + num1 (float): Primer número. + num2 (float): Segundo número. + + Returns: + int: Resultado de la multiplicación. + + Note: + Debe redondear los números recibidos a enteros para trabajar. + """ + # TODO: + # 1. Realizar el desarrollo completo de esta función teniendo en cuenta la documentación. + # 2. Esta función debe pasar las pruebas unitarias. + # 3. No podéis usar el operador de multiplicación de Python para realizar el desarrollo de la misma, + # es decir, que debéis realizar la multiplicación con SUMAS y/o RESTAS... + # 4. Aunque se reciben números de tipo float, debéis redondearlos como números enteros para + # simplificar esta función, es decir, la operación 4.98 * 3.33, deberá convertirse en 5 * 3. + # 5. Tened en cuenta que podéis recibir números negativos, es decir, la operación -5 * -5 = 25 + # 6. OBLIGATORIO usar un bucle for. + # 7. Incluir algún comentario para mejorar la claridad y permitir que otros comprendan el propósito y funcionamiento del código. + for i in range(1,num1,num2): + print + + + +def dividir(): + """ + Realiza la división ENTERA de dos números usando solo sumas y restas. + + Args: + num1 (float): Dividendo. + num2 (float): Divisor. + + Returns: + int: Resultado de la división. + + Raises: + ZeroDivisionError: Si el divisor es cero. + + Note: + Debe redondear los números recibidos a enteros para trabajar. + """ + # TODO: + # 1. Realizar el desarrollo completo de esta función teniendo en cuenta la documentación. + # 2. Esta función debe pasar las pruebas unitarias. + # 3. No podéis usar el operador de división de Python para realizar el desarrollo de la misma, + # es decir, que debéis realizar la división con SUMAS y/o RESTAS... + # 4. Aunque se reciben números de tipo float, debéis redondearlos como números enteros para + # simplificar esta función, es decir, la operación 4.98 / 3.33, deberá convertirse en 5 / 3. + # 5. Tened en cuenta que podéis recibir números negativos, es decir, la operación -5 / -5 = 1 + # 6. Incluir algún comentario para mejorar la claridad y permitir que otros comprendan el propósito y funcionamiento del código. + + +def potencia(): + a + # TODO: + # 1. Realizar el desarrollo completo de esta función y su documentación. + # 2. Esta función debe pasar las pruebas unitarias. + # 3. PREMISAS a tener en cuenta: + # - Cualquier número elevado a 0 da como resultado 1. + # - Para simplificar esta función, vamos a suponer que un número elevado a un + # exponente negativo siempre dará 0 (aunque en realidad no es así matemáticamente) + # 4. Utiliza la función de multiplicar para realizar los cálculos que te + # harán falta en esta función (RECUERDA que no puedes usar directamente los operadores + # de Python para la multiplicación y división). + # 5. Incluir algún comentario para mejorar la claridad y permitir que otros comprendan el propósito y funcionamiento del código. + + +def pedir_entrada(msj: str) -> str: + """ + Pide al usuario una entrada, elimina espacios por delante y por detrás y la convierte a minúsculas. + + Args: + msj (str): Mensaje para solicitar la entrada. + + Returns: + str: Entrada del usuario. + """ + # TODO: El desarrollo de esta función está incompleto, debéis terminarla teniendo en cuenta la documentación + msj = (msj).strip().lower() + return input(msj) + + +def calcular_operacion(num1: float, num2: float, operador: str) -> float: + """ + Realiza la operación especificada entre num1 y num2 dependiendo del valor del operador. + + Args: + num1 (float): Primer número. + num2 (float): Segundo número. + operador (str): Operador de la operación. + + Returns: + float: Resultado de la operación. + + Raises: + ZeroDivisionError: Si el divisor es cero. + """ + # TODO: El desarrollo de esta función está incompleto... completadla teniendo en cuenta la documentación + # y que debe realizar las llamadas adecuadas a las funciones ya creadas para realizar los distintos + # cálculos... sumar, restar, multiplicar, dividir y potencia. + # IMPORTANTE: Si hacemos caso a la documentación de esta función, NO debéis capturar la excepción + # ZeroDivisionError aquí, sino que hay que dejadla que se propague a la función llamante, realizar_calculo(), + # dónde se os indica cómo realizar la gestión de estos errores. + resultado = "0.0" + operador = pedir_entrada() + if operador in ['+', '-', '*', 'x', '/', ':', '**', 'exp']: + operador = OPERADORES + else: + # Convertir la entrada a número usando float para realizar las operaciones + num1 = float(pedir_entrada) + num1 = float(pedir_entrada) + + if resultado == "0.0": + calculo_actual = num1 # Primer número ingresado + else: + # Realizar la operación dependiendo del operador + if operador == '+': + + sumar(resultado, num1, num2) + elif operador == '-': + restar(resultado, num1, num2) + elif operador in ['*', 'x']: + multiplicar(resultado, num1, num2) + elif operador in ['/', ':']: + if num2 == 0 or num1 == 0: + raise ZeroDivisionError() + else: + print("Error: División por cero.") + + elif operador in ['**', 'exp']: + calculo_actual = str(float(calculo_actual) ** num1, num2) + + return resultado + + + +def obtener_operaciones() -> str: + """ + Devuelve una cadena con la lista de operaciones disponibles en la calculadora. + + Returns: + (str): cadena de caracteres con la información de las operaciones disponibles. + """ + # TODO: El desarrollo de esta función está incompleto... ver documentación para solucionarla correctamente. + """ + Operaciones disponibles: + ce => Reiniciar resultado a 0 + decimales => Establecer decimales en resultado + cadena vacía + => Pregunta si desea salir + calculo => Iniciar cálculo secuencial + + => Suma + - => Resta + x o * => Multiplicación + / o : => División + ** o exp => Potencia + cancelar => vovler sin actualizar resultado de la calculadora + cadena vacía + => volver actualizando resultado de la calculadora + """ + print("\nOperaciones disponibles:") + print(" ce => Reiniciar resultado a 0") + print(" decimales => Establecer decimales en resultado") + print(" lista => Ver lista de operaciones") + print(" calculo => Iniciar cálculo secuencial") + print(" + => Suma") + print(" - => Resta") + print(" x o * => Multiplicación") + print(" / o : => División") + print(" ** o exp => Potencia") + print(" cancelar => Volver sin actualizar resultado de la calculadora") + print(" cadena vacía + => Volver actualizando resultado de la calculadora") + +def realizar_calculo(): + """ + Realiza una secuencia de cálculos solicitando números y operadores al usuario. + + Args: + decimales (int): Número de decimales para el resultado. + resultado_almacenado (float): Valor almacenado en la calculadora. + + Returns: + float: Resultado final del cálculo o None si se cancela. + + Note: + * Dentro de esta función el usuario puede realizar cálculos secuenciales, es decir, + comenzará introduciendo un número, después un operador, y otro número... a partir + de aquí sobre el resultado acumulado, introducirá operador y número para seguir + realizando cálculos (ver ejemplos en README.md de la tarea en el repositorio de GitHub). + * El usuario es guiado para introducir números y operadores secuencialmente + para realizar operaciones básicas. + * El usuario puede utilizar "resultado" en la secuencia de cálculo para reutilizar el + resultado almacenado en la calculadora. + * El cálculo finaliza al pulsar , volviendo y actualizando el resultado almacenado + de la calculadora con el cálculo realizado. + * También podemos escribir "cancelar", volviendo sin realizar ningún cambio en el + resultado almacenado de la calculadora. + """ + # TODO: El desarrollo de esta función está incompleto... ver documentación para solucionarla correctamente. + + operador = None + resultado = None + realizando_calculos = True + resultado_almacenado = "0.0" + decimales = 2 + print("\n## Ingrese número, operador, 'resultado', 'cancelar' o para finalizar el cálculo ##\n") + + while realizando_calculos: + entrada = pedir_entrada(f"\t (Cálculo = resultado) >> ") + + if entrada == "cancelar": + print("Secuencia de calculo cancelada.") + break + + + elif entrada == "": + resultado = None + + + elif entrada in OPERADORES: + operador = calcular_operacion() + + else: + # TODO: este código funciona cuando solucionéis que reconozca las variables resultado_almacenado y decimales. + # Pero no gestiona los posibles tipos de Excepciones que se pueden producir: + # - ValueError que debe mostrar el error que está en la posición 2 de MENSAJES_ERROR. + # - ZeroDivisionError que debe mostrar el error que está en la posición 5 de MENSAJES_ERROR. + # - Exception que debe mostrar el error que está en la posición 6 de MENSAJES_ERROR. + if entrada == "resultado": + entrada = resultado_almacenado + + try: + numero = float(entrada) + + if operador is not None: + if resultado is None: + resultado = 0 + resultado = round(calcular_operacion(resultado, numero, operador), decimales) + operador = None + + elif resultado is None: + resultado = numero + else: + mostrar_error(3) + except: + print + + +def main(): + """ + Función principal de la calculadora. Gestiona la entrada del usuario y coordina las operaciones. + + Note: + El flujo del programa es el siguiente: + + 1. Inicia la calculadora mostrando el resultado almacenado por defecto (0.00). + + 2. El usuario ingresa un comando, que puede ser: + - "lista" para ver todas las operaciones disponibles. + - "ce" para reiniciar el resultado almacenado a 0. + - "decimales " para establecer el número de decimales mostrados en el resultado. + - "calculo" para iniciar una secuencia de cálculo paso a paso. + - Una entrada vacía y pulsa la tecla para salir de la calculadora. + + 3. Según el comando ingresado: + - El programa realiza la operación o ejecuta la acción indicada. + - Al ingresar "calculo": + * El usuario es guiado para introducir números y operadores secuencialmente para realizar operaciones básicas. + * El usuario puede utilizar "resultado" en la secuencia de cálculo para reutilizar el resultado almacenado en la calculadora. + * El cálculo finaliza al pulsar , volviendo y actualizando el resultado almacenado de la calculadora con el cálculo realizado. + * También podemos escribir "cancelar", volviendo sin realizar ningún cambio en el resultado almacenado de la calculadora. + + 4. La calculadora sigue ejecutándose hasta que el usuario confirma la salida al ingresar una entrada vacía y pulsar . + + 5. Finalmente, se limpia la pantalla, el programa se despide con el mensaje "\n\nBye, bye...\n\n" y termina. + """ + # TODO: Corrige los errores y haz que el main funcione correctamente... + + decimales = 2 + desea_salir = False + while not desea_salir: + + entrada = pedir_entrada(f"Operación (RES => resultado) >> ") + + if entrada == "": + pedir_entrada("¿Desea salir de la calculadora? (s/n) ") + if entrada == "s": + limpiar_pantalla() + print("\n\nBye, bye...\n\n") + break + + elif entrada == "lista": + obtener_operaciones() + + + elif entrada == "ce": + global resultado + resultado = "0.0" + print(f"Resultado reiniciado a {resultado}.") + + + + elif entrada.startswith("decimales"): + # Extraemos las posiciones decimales y las convertimos a un valor entero + decimales = str(entrada.split()[1]) + print(f"Decimales configurados a {decimales}.") + + elif entrada == "calculo": + realizar_calculo(decimales, resultado) + + else: + mostrar_error(5) + + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/dam1_calculadora/bruno_calculadora_alumnos.py b/dam1_calculadora/bruno_calculadora_alumnos.py new file mode 100644 index 0000000..342e5e9 --- /dev/null +++ b/dam1_calculadora/bruno_calculadora_alumnos.py @@ -0,0 +1,356 @@ +# TODO: ATENCIÓN!!! +# 1. Importar los paquetes necesarios +# 2. Usar la función mostrar_error para imprimir los errores por consola. +# 3. Los comentarios TODO os ayudan a resolver cada apartado de esta prueba. +# 4. CADA función SOLO puede tener una instrucción return. +# 5. En test_calculadora_alumnos.py crear el test unitario para la función es_resultado_negativo y hacer que todos los test unitarios se cumplan. + + + +# Mensajes de error predefinidos +MENSAJES_ERROR = ( + "Problemas al intentar limpiar la pantalla {error}", + "Error al configurar los decimales. Formato: decimales .", + "Entrada no válida. Ingrese número, operador, 'resultado', 'cancelar' o para finalizar el cálculo.", + "Error: Introduzca un operador antes de otro número.", + "Comando no reconocido. Escriba 'lista' para ver las operaciones disponibles.", + "Error: no es posible la división por 0! Introduzca otro valor diferente a 0...", + "Se produjo un error: {error}" +) + +# Operadores soportados por la calculadora +OPERADORES = +# TODO: Ayuda: incluye en esta constante los símbolos que reconocerá la aplicación para el cálculo de las operaciones: +# '+' para la suma. +# '-' para la resta. +# 'x' o '*' para la multiplicación. +# '/' o ':' para la división. +# '**' o 'exp' para la potencia. + + + +def limpiar_pantalla(): + """ + Limpia la consola según el sistema operativo. + """ + # TODO: El desarrollo de esta función está incompleto y con errores. + try: + os.system(clear if os.name = posix else cls) + # Otra forma de expresar la misma instrucción sería la siguiente: + # if os.name = posix: + # os.system(clear) + # else: + # os.system(cls) + except Exception as e: + + + +def pausa(): + """ + Pausa la ejecución del programa hasta que se pulse ENTER... "\nPresione ENTER para continuar..." + """ + # TODO: Desarrollar esta función según su documentación. + + +def mostrar_error(indice_error: int, msj_error = None): + """ + Muestra un mensaje de error en la consola. + + Args: + indice_error (int): Índice del mensaje de error en MENSAJES_ERROR. + msj_error (str, opcional): Texto adicional para personalizar el mensaje de error. + """ + # TODO: + # 1. Corrige el error que existe a la hora de acceder a los mensajes de error que están en la constante + # MENSAJES_ERROR. A los elementos de una tupla se accede igual que a los caracteres de una + # cadena de caracteres. + # 2. Completa el código de esta función para que controle específicamente las excepciones IndexError y + # muestre el mensaje: "\n*ERROR* Mensaje de error no definido.\n" + # 3. También se pide que se controle cualquier otra excepción que se pueda producir y muestre el mensaje: + # "\n*ERROR* Problemas al mostrar error!\n{e}\n" + # 4. En esta función los mensajes de error deben mostrarse con print. + if msj_error != None: + print(f"\n*ERROR* {MENSAJES_ERROR.format(error = msj_error)}\n") + else: + print(f"\n*ERROR* {MENSAJES_ERROR}\n") + + +def sumar(): + # TODO: Realizar el desarrollo completo, incluida la documentación... recibe 2 números float y retorna la suma de ambos. + + + +def restar(): + # TODO: Realizar el desarrollo completo, incluida la documentación... recibe 2 números float y retorna la resta de ambos. + + + +def es_resultado_negativo(num1: float, num2: float) -> bool: + """Determina si el resultado de una operación entre num1 y num2 debe ser negativo.""" + # TODO: Realizar el desarrollo completo de esta función teniendo en cuenta la documentación y completándola también. + # Debe pasar las pruebas unitarias. + # Se trata de una función que os puede venir bien para utilizarla tanto en la función multiplicar, como en dividir. + # Ya que va a determinar si la multiplicación o división entre dos números debería ser de signo negativo o no. + + + +def multiplicar(): + """ + Realiza la multiplicación ENTERA de dos números usando solo sumas y restas. + + Args: + num1 (float): Primer número. + num2 (float): Segundo número. + + Returns: + int: Resultado de la multiplicación. + + Note: + Debe redondear los números recibidos a enteros para trabajar. + """ + # TODO: + # 1. Realizar el desarrollo completo de esta función teniendo en cuenta la documentación. + # 2. Esta función debe pasar las pruebas unitarias. + # 3. No podéis usar el operador de multiplicación de Python para realizar el desarrollo de la misma, + # es decir, que debéis realizar la multiplicación con SUMAS y/o RESTAS... + # 4. Aunque se reciben números de tipo float, debéis redondearlos como números enteros para + # simplificar esta función, es decir, la operación 4.98 * 3.33, deberá convertirse en 5 * 3. + # 5. Tened en cuenta que podéis recibir números negativos, es decir, la operación -5 * -5 = 25 + # 6. OBLIGATORIO usar un bucle for. + # 7. Incluir algún comentario para mejorar la claridad y permitir que otros comprendan el propósito y funcionamiento del código. + + + +def dividir(): + """ + Realiza la división ENTERA de dos números usando solo sumas y restas. + + Args: + num1 (float): Dividendo. + num2 (float): Divisor. + + Returns: + int: Resultado de la división. + + Raises: + ZeroDivisionError: Si el divisor es cero. + + Note: + Debe redondear los números recibidos a enteros para trabajar. + """ + # TODO: + # 1. Realizar el desarrollo completo de esta función teniendo en cuenta la documentación. + # 2. Esta función debe pasar las pruebas unitarias. + # 3. No podéis usar el operador de división de Python para realizar el desarrollo de la misma, + # es decir, que debéis realizar la división con SUMAS y/o RESTAS... + # 4. Aunque se reciben números de tipo float, debéis redondearlos como números enteros para + # simplificar esta función, es decir, la operación 4.98 / 3.33, deberá convertirse en 5 / 3. + # 5. Tened en cuenta que podéis recibir números negativos, es decir, la operación -5 / -5 = 1 + # 6. Incluir algún comentario para mejorar la claridad y permitir que otros comprendan el propósito y funcionamiento del código. + + +def potencia(): + # TODO: + # 1. Realizar el desarrollo completo de esta función y su documentación. + # 2. Esta función debe pasar las pruebas unitarias. + # 3. PREMISAS a tener en cuenta: + # - Cualquier número elevado a 0 da como resultado 1. + # - Para simplificar esta función, vamos a suponer que un número elevado a un + # exponente negativo siempre dará 0 (aunque en realidad no es así matemáticamente) + # 4. Utiliza la función de multiplicar para realizar los cálculos que te + # harán falta en esta función (RECUERDA que no puedes usar directamente los operadores + # de Python para la multiplicación y división). + # 5. Incluir algún comentario para mejorar la claridad y permitir que otros comprendan el propósito y funcionamiento del código. + + +def pedir_entrada(msj: str) -> str: + """ + Pide al usuario una entrada, elimina espacios por delante y por detrás y la convierte a minúsculas. + + Args: + msj (str): Mensaje para solicitar la entrada. + + Returns: + str: Entrada del usuario. + """ + # TODO: El desarrollo de esta función está incompleto, debéis terminarla teniendo en cuenta la documentación + return input(msj) + + +def calcular_operacion(num1: float, num2: float, operador: str) -> float: + """ + Realiza la operación especificada entre num1 y num2 dependiendo del valor del operador. + + Args: + num1 (float): Primer número. + num2 (float): Segundo número. + operador (str): Operador de la operación. + + Returns: + float: Resultado de la operación. + + Raises: + ZeroDivisionError: Si el divisor es cero. + """ + # TODO: El desarrollo de esta función está incompleto... completadla teniendo en cuenta la documentación + # y que debe realizar las llamadas adecuadas a las funciones ya creadas para realizar los distintos + # cálculos... sumar, restar, multiplicar, dividir y potencia. + # IMPORTANTE: Si hacemos caso a la documentación de esta función, NO debéis capturar la excepción + # ZeroDivisionError aquí, sino que hay que dejadla que se propague a la función llamante, realizar_calculo(), + # dónde se os indica cómo realizar la gestión de estos errores. + + return resultado + + + +def obtener_operaciones() -> str: + """ + Devuelve una cadena con la lista de operaciones disponibles en la calculadora. + + Returns: + (str): cadena de caracteres con la información de las operaciones disponibles. + """ + # TODO: El desarrollo de esta función está incompleto... ver documentación para solucionarla correctamente. + """ + Operaciones disponibles: + ce => Reiniciar resultado a 0 + decimales => Establecer decimales en resultado + cadena vacía + => Pregunta si desea salir + calculo => Iniciar cálculo secuencial + + => Suma + - => Resta + x o * => Multiplicación + / o : => División + ** o exp => Potencia + cancelar => vovler sin actualizar resultado de la calculadora + cadena vacía + => volver actualizando resultado de la calculadora + """ + + +def realizar_calculo(): + """ + Realiza una secuencia de cálculos solicitando números y operadores al usuario. + + Args: + decimales (int): Número de decimales para el resultado. + resultado_almacenado (float): Valor almacenado en la calculadora. + + Returns: + float: Resultado final del cálculo o None si se cancela. + + Note: + * Dentro de esta función el usuario puede realizar cálculos secuenciales, es decir, + comenzará introduciendo un número, después un operador, y otro número... a partir + de aquí sobre el resultado acumulado, introducirá operador y número para seguir + realizando cálculos (ver ejemplos en README.md de la tarea en el repositorio de GitHub). + * El usuario es guiado para introducir números y operadores secuencialmente + para realizar operaciones básicas. + * El usuario puede utilizar "resultado" en la secuencia de cálculo para reutilizar el + resultado almacenado en la calculadora. + * El cálculo finaliza al pulsar , volviendo y actualizando el resultado almacenado + de la calculadora con el cálculo realizado. + * También podemos escribir "cancelar", volviendo sin realizar ningún cambio en el + resultado almacenado de la calculadora. + """ + # TODO: El desarrollo de esta función está incompleto... ver documentación para solucionarla correctamente. + + operador = None + resultado = None + realizando_calculos = True + + print("\n## Ingrese número, operador, 'resultado', 'cancelar' o para finalizar el cálculo ##\n") + + while realizando_calculos: + entrada = pedir_entrada(f"\t (Cálculo = resultado) >> ") + + if entrada == "cancelar": + + + elif entrada == "": + + + elif entrada in OPERADORES: + operador = + + else: + # TODO: este código funciona cuando solucionéis que reconozca las variables resultado_almacenado y decimales. + # Pero no gestiona los posibles tipos de Excepciones que se pueden producir: + # - ValueError que debe mostrar el error que está en la posición 2 de MENSAJES_ERROR. + # - ZeroDivisionError que debe mostrar el error que está en la posición 5 de MENSAJES_ERROR. + # - Exception que debe mostrar el error que está en la posición 6 de MENSAJES_ERROR. + if entrada == "resultado": + entrada = resultado_almacenado + + try: + numero = float(entrada) + + if operador is not None: + if resultado is None: + resultado = 0 + resultado = round(calcular_operacion(resultado, numero, operador), decimales) + operador = None + + elif resultado is None: + resultado = numero + + else: + mostrar_error(3) + except + + +def main(): + """ + Función principal de la calculadora. Gestiona la entrada del usuario y coordina las operaciones. + + Note: + El flujo del programa es el siguiente: + + 1. Inicia la calculadora mostrando el resultado almacenado por defecto (0.00). + + 2. El usuario ingresa un comando, que puede ser: + - "lista" para ver todas las operaciones disponibles. + - "ce" para reiniciar el resultado almacenado a 0. + - "decimales " para establecer el número de decimales mostrados en el resultado. + - "calculo" para iniciar una secuencia de cálculo paso a paso. + - Una entrada vacía y pulsa la tecla para salir de la calculadora. + + 3. Según el comando ingresado: + - El programa realiza la operación o ejecuta la acción indicada. + - Al ingresar "calculo": + * El usuario es guiado para introducir números y operadores secuencialmente para realizar operaciones básicas. + * El usuario puede utilizar "resultado" en la secuencia de cálculo para reutilizar el resultado almacenado en la calculadora. + * El cálculo finaliza al pulsar , volviendo y actualizando el resultado almacenado de la calculadora con el cálculo realizado. + * También podemos escribir "cancelar", volviendo sin realizar ningún cambio en el resultado almacenado de la calculadora. + + 4. La calculadora sigue ejecutándose hasta que el usuario confirma la salida al ingresar una entrada vacía y pulsar . + + 5. Finalmente, se limpia la pantalla, el programa se despide con el mensaje "\n\nBye, bye...\n\n" y termina. + """ + # TODO: Corrige los errores y haz que el main funcione correctamente... + + decimales = 2 + resultado = 0.0 + + while not desea_salir: + + pedir_entrada(f"Operación (RES => resultado) >> ") + + if entrada == "": + pedir_entrada("¿Desea salir de la calculadora? (s/n) ") + + elif entrada == "lista": + obtener_operaciones + + elif entrada == "ce": + + + elif entrada.startswith("decimales"): + # Extraemos las posiciones decimales y las convertimos a un valor entero + decimales = str(entrada.split()[1]) + print(f"Decimales configurados a {decimales}.") + + elif entrada == "calculo": + realizar_calculo(decimales, resultado) + + else: + mostrar_error diff --git a/dam1_calculadora/carame_calculadora_alumnos.py b/dam1_calculadora/carame_calculadora_alumnos.py new file mode 100644 index 0000000..5d4438f --- /dev/null +++ b/dam1_calculadora/carame_calculadora_alumnos.py @@ -0,0 +1,475 @@ +# TODO: ATENCIÓN!!! +# 1. Importar los paquetes necesarios +# 2. Usar la función mostrar_error para imprimir los errores por consola. +# 3. Los comentarios TODO os ayudan a resolver cada apartado de esta prueba. +# 4. CADA función SOLO puede tener una instrucción return. +# 5. En test_calculadora_alumnos.py crear el test unitario para la función es_resultado_negativo y hacer que todos los test unitarios se cumplan. + +import os +import time + +# Mensajes de error predefinidos +MENSAJES_ERROR = ( + "Problemas al intentar limpiar la pantalla {error}", + "Error al configurar los decimales. Formato: decimales .", + "Entrada no válida. Ingrese número, operador, 'resultado', 'cancelar' o para finalizar el cálculo.", + "Error: Introduzca un operador antes de otro número.", + "Comando no reconocido. Escriba 'lista' para ver las operaciones disponibles.", + "Error: no es posible la división por 0! Introduzca otro valor diferente a 0...", + "Se produjo un error: {error}" +) + +# Operadores soportados por la calculadora +OPERADORES = ("+","-","x","*","/",":","**","exp","^") +# TODO: Ayuda: incluye en esta constante los símbolos que reconocerá la aplicación para el cálculo de las operaciones: +# '+' para la suma. +# '-' para la resta. +# 'x' o '*' para la multiplicación. +# '/' o ':' para la división. +# '**' o 'exp' para la potencia. + + + +def limpiar_pantalla(): + """ + Limpia la consola según el sistema operativo. + """ + # TODO: El desarrollo de esta función está incompleto y con errores. + try: + + # Otra forma de expresar la misma instrucción sería la siguiente: + if os.name == "posix": + os.system("clear") + else: + os.system("cls") + except Exception as e: + + + +def pausa(tecla_enter : False): + """ + Pausa la ejecución del programa hasta que se pulse ENTER... "\nPresione ENTER para continuar..." + """ + # TODO: Desarrollar esta función según su documentación. + os.system("Pause") + if tecla_enter: + input("Presione ENTER para continuar...") + + +def mostrar_error(indice_error: int, msj_error = None): + """ + Muestra un mensaje de error en la consola. + + Args: + indice_error (int): Índice del mensaje de error en MENSAJES_ERROR. + msj_error (str, opcional): Texto adicional para personalizar el mensaje de error. + """ + # TODO: + # 1. Corrige el error que existe a la hora de acceder a los mensajes de error que están en la constante + # MENSAJES_ERROR. A los elementos de una tupla se accede igual que a los caracteres de una + # cadena de caracteres. + # 2. Completa el código de esta función para que controle específicamente las excepciones IndexError y + # muestre el mensaje: "\n*ERROR* Mensaje de error no definido.\n" + # 3. También se pide que se controle cualquier otra excepción que se pueda producir y muestre el mensaje: + # "\n*ERROR* Problemas al mostrar error!\n{e}\n" + # 4. En esta función los mensajes de error deben mostrarse con print. + for indice_error in MENSAJES_ERROR: + if indice_error == 1: + print("Problemas al intentar limpiar la pantalla {error}") + elif indice_error == 2: + print("Error al configurar los decimales. Formato: decimales .") + elif indice_error == 3: + print("Entrada no válida. Ingrese número, operador, 'resultado', 'cancelar' o para finalizar el cálculo.") + elif indice_error == 4: + print("Error: Introduzca un operador antes de otro número.") + elif indice_error == 5: + print("Comando no reconocido. Escriba 'lista' para ver las operaciones disponibles.") + elif indice_error == 6: + print("Error: no es posible la división por 0! Introduzca otro valor diferente a 0...") + elif indice_error == 7: + print("Se produjo un error: {error}") + if msj_error != None: + print(f"\n*ERROR* {MENSAJES_ERROR.format(error = msj_error)}\n") + else: + print(f"\n*ERROR* {MENSAJES_ERROR}\n") + + +def sumar(num1 : float, num2 : float): + """ + Realiza la suma de dos números. + Args: + num1 (float): Primer número. + num2 (float): Segundo número. + Returns: + int: Resultado de la suma. + """ + # TODO: Realizar el desarrollo completo, incluida la documentación... recibe 2 números float y retorna la suma de ambos. + resultado = num1 + num2 + return resultado + + +def restar(num1 : float, num2 : float): + """ + Realiza la resta de dos números. + Args: + num1 (float): Primer número. + num2 (float): Segundo número. + Returns: + int: Resultado de la resta. + """ + # TODO: Realizar el desarrollo completo, incluida la documentación... recibe 2 números float y retorna la resta de ambos. + resultado = num1 - num2 + return resultado + + +def es_resultado_negativo(num1: float, num2: float) -> bool: + """Determina si el resultado de una operación entre num1 y num2 debe ser negativo.""" + # TODO: Realizar el desarrollo completo de esta función teniendo en cuenta la documentación y completándola también. + # Debe pasar las pruebas unitarias. + # Se trata de una función que os puede venir bien para utilizarla tanto en la función multiplicar, como en dividir. + # Ya que va a determinar si la multiplicación o división entre dos números debería ser de signo negativo o no. + if num1 or num2 < 0: + return True + else: + return False + +def multiplicar(num1 : float,num2 : float)->int : + """ + Realiza la multiplicación ENTERA de dos números usando solo sumas y restas. + + Args: + num1 (float): Primer número. + num2 (float): Segundo número. + + Returns: + int: Resultado de la multiplicación. + + Note: + Debe redondear los números recibidos a enteros para trabajar. + """ + # TODO: + # 1. Realizar el desarrollo completo de esta función teniendo en cuenta la documentación. + # 2. Esta función debe pasar las pruebas unitarias. + # 3. No podéis usar el operador de multiplicación de Python para realizar el desarrollo de la misma, + # es decir, que debéis realizar la multiplicación con SUMAS y/o RESTAS... + # 4. Aunque se reciben números de tipo float, debéis redondearlos como números enteros para + # simplificar esta función, es decir, la operación 4.98 * 3.33, deberá convertirse en 5 * 3. + # 5. Tened en cuenta que podéis recibir números negativos, es decir, la operación -5 * -5 = 25 + # 6. OBLIGATORIO usar un bucle for. + # 7. Incluir algún comentario para mejorar la claridad y permitir que otros comprendan el propósito y funcionamiento del código. + round(num1) + round(num2) + n1 = num1 #Guardo los valores originales de num1 + n2 = num2 #Guardo los valores originales de num2 + if num2 < 0: #Quito los negativos de num2 si hay + num2 = str(num2) + num2 = (num2[1]) + num2 = float(num2) + if num1 < 0: #Quito los negativos de num1 si hay + num1 = str(num1) + num1 = (num1[1]) + num1 = float(num1) + resultado = 0 + for i in range (0,int(num2),1): + resultado = resultado + num1 + if not es_resultado_negativo(n1,n2): #Saco si el resultado es negativo o positivo gracias a los valores antes recibidos + return int(resultado) + else: + return int(-resultado) + +#Esto es solo para el commit + +def dividir(num1 : float,num2 : float): + """ + Realiza la división ENTERA de dos números usando solo sumas y restas. + + Args: + num1 (float): Dividendo. + num2 (float): Divisor. + + Returns: + int: Resultado de la división. + + Raises: + ZeroDivisionError: Si el divisor es cero. + + Note: + Debe redondear los números recibidos a enteros para trabajar. + """ + # TODO: + # 1. Realizar el desarrollo completo de esta función teniendo en cuenta la documentación. + # 2. Esta función debe pasar las pruebas unitarias. + # 3. No podéis usar el operador de división de Python para realizar el desarrollo de la misma, + # es decir, que debéis realizar la división con SUMAS y/o RESTAS... + # 4. Aunque se reciben números de tipo float, debéis redondearlos como números enteros para + # simplificar esta función, es decir, la operación 4.98 / 3.33, deberá convertirse en 5 / 3. + # 5. Tened en cuenta que podéis recibir números negativos, es decir, la operación -5 / -5 = 1 + # 6. Incluir algún comentario para mejorar la claridad y permitir que otros comprendan el propósito y funcionamiento del código. + round(num1) + round(num2) + n1 = num1 #Guardo los valores originales de num1 + n2 = num2 #Guardo los valores originales de num2 + if num2 < 0: #Quito los negativos de num2 si hay + num2 = str(num2) + num2 = (num2[1]) + num2 = float(num2) + if num1 < 0: #Quito los negativos de num1 si hay + num1 = str(num1) + num1 = (num1[1]) + num1 = float(num1) + resultado = 0 + num1 = num1 - num2 + while num1 >= 0: + resultado = resultado + 1 + num1 = num1 - num2 + if not es_resultado_negativo(n1,n2): #Saco si el resultado es negativo o positivo gracias a los valores antes recibidos + return int(resultado) + else: + return int(-resultado) + +def potencia(num1:float,num2:float): + """ + Realiza la potencia de un número elevado a otro. + Args: + num1 (float): Primer número. + num2 (float): Segundo número. + Returns: + int: Resultado de la potencia. + """ + # TODO: + # 1. Realizar el desarrollo completo de esta función y su documentación. + # 2. Esta función debe pasar las pruebas unitarias. + # 3. PREMISAS a tener en cuenta: + # - Cualquier número elevado a 0 da como resultado 1. + # - Para simplificar esta función, vamos a suponer que un número elevado a un + # exponente negativo siempre dará 0 (aunque en realidad no es así matemáticamente) + # 4. Utiliza la función de multiplicar para realizar los cálculos que te + # harán falta en esta función (RECUERDA que no puedes usar directamente los operadores + # de Python para la multiplicación y división). + # 5. Incluir algún comentario para mejorar la claridad y permitir que otros comprendan el propósito y funcionamiento del código. + round(num1) + round(num2) + resultado = 1 + for i in range(0,int(num2)):#Bucle for para hacer la potencia + resultado = multiplicar(resultado,num1)#Llama a la funcion multiplicar + if not es_resultado_negativo(num1,num2):#Llama a la funcion es_resultado_negativo + return int(resultado) + else: + return int(-resultado) + +def pedir_entrada(msj: str) -> str: + """ + Pide al usuario una entrada, elimina espacios por delante y por detrás y la convierte a minúsculas. + + Args: + msj (str): Mensaje para solicitar la entrada. + + Returns: + str: Entrada del usuario. + """ + # TODO: El desarrollo de esta función está incompleto, debéis terminarla teniendo en cuenta la documentación + + return input(msj).strip().lower() + + +def calcular_operacion(num1: float, num2: float, operador: str) -> float: + """ + Realiza la operación especificada entre num1 y num2 dependiendo del valor del operador. + + Args: + num1 (float): Primer número. + num2 (float): Segundo número. + operador (str): Operador de la operación. + + Returns: + float: Resultado de la operación. + + Raises: + ZeroDivisionError: Si el divisor es cero. + """ + # TODO: El desarrollo de esta función está incompleto... completadla teniendo en cuenta la documentación + # y que debe realizar las llamadas adecuadas a las funciones ya creadas para realizar los distintos + # cálculos... sumar, restar, multiplicar, dividir y potencia. + # IMPORTANTE: Si hacemos caso a la documentación de esta función, NO debéis capturar la excepción + # ZeroDivisionError aquí, sino que hay que dejadla que se propague a la función llamante, realizar_calculo(), + # dónde se os indica cómo realizar la gestión de estos errores. + for operador in OPERADORES: + if operador == "+": + resultado = sumar(num1,num2) + elif operador == "-": + resultado = restar(num1,num2) + elif operador == "x" or "*": + resultado = multiplicar(num1,num2) + elif operador == ":" or "/": + resultado = dividir(num1,num2) + elif operador == "**" or "exp" or "^": + resultado = potencia(num1,num2) + else: + pass + return resultado + + + +def obtener_operaciones() -> str: + """ + Devuelve una cadena con la lista de operaciones disponibles en la calculadora. + + Returns: + (str): cadena de caracteres con la información de las operaciones disponibles. + """ + # TODO: El desarrollo de esta función está incompleto... ver documentación para solucionarla correctamente. + """ + Operaciones disponibles: + ce => Reiniciar resultado a 0 + decimales => Establecer decimales en resultado + cadena vacía + => Pregunta si desea salir + calculo => Iniciar cálculo secuencial + + => Suma + - => Resta + x o * => Multiplicación + / o : => División + ** o exp => Potencia + cancelar => vovler sin actualizar resultado de la calculadora + cadena vacía + => volver actualizando resultado de la calculadora + """ + print("Operaciones disponibles:") + print("ce => Reiniciar resultado a 0") + print("decimales => Establecer decimales en resultado") + print("cadena vacía + => Pregunta si desea salir") + print("calculo => Iniciar cálculo secuencial") + print(" + => Suma") + print(" - => Resta") + print(" x o * => Multiplicación") + print(" / o : => División") + print(" ** , exp o ^ => Potencia") + print(" cancelar => vovler sin actualizar resultado de la calculadora") + print(" cadena vacía + => volver actualizando resultado de la calculadora") + + +def realizar_calculo(decimales :int,resultado_almacenado:float): + """ + Realiza una secuencia de cálculos solicitando números y operadores al usuario. + + Args: + decimales (int): Número de decimales para el resultado. + resultado_almacenado (float): Valor almacenado en la calculadora. + + Returns: + float: Resultado final del cálculo o None si se cancela. + + Note: + * Dentro de esta función el usuario puede realizar cálculos secuenciales, es decir, + comenzará introduciendo un número, después un operador, y otro número... a partir + de aquí sobre el resultado acumulado, introducirá operador y número para seguir + realizando cálculos (ver ejemplos en README.md de la tarea en el repositorio de GitHub). + * El usuario es guiado para introducir números y operadores secuencialmente + para realizar operaciones básicas. + * El usuario puede utilizar "resultado" en la secuencia de cálculo para reutilizar el + resultado almacenado en la calculadora. + * El cálculo finaliza al pulsar , volviendo y actualizando el resultado almacenado + de la calculadora con el cálculo realizado. + * También podemos escribir "cancelar", volviendo sin realizar ningún cambio en el + resultado almacenado de la calculadora. + """ + # TODO: El desarrollo de esta función está incompleto... ver documentación para solucionarla correctamente. + + operador = None + resultado = None + realizando_calculos = True + + print("\n## Ingrese número, operador, 'resultado', 'cancelar' o para finalizar el cálculo ##\n") + + while realizando_calculos: + entrada = pedir_entrada(f"\t (Cálculo = resultado) >> ") + + if entrada == "cancelar": + + + elif entrada == "": + + + elif entrada in OPERADORES: + operador = + + else: + # TODO: este código funciona cuando solucionéis que reconozca las variables resultado_almacenado y decimales. + # Pero no gestiona los posibles tipos de Excepciones que se pueden producir: + # - ValueError que debe mostrar el error que está en la posición 2 de MENSAJES_ERROR. + # - ZeroDivisionError que debe mostrar el error que está en la posición 5 de MENSAJES_ERROR. + # - Exception que debe mostrar el error que está en la posición 6 de MENSAJES_ERROR. + if entrada == "resultado": + entrada = resultado_almacenado + + try: + numero = float(entrada) + + if operador is not None: + if resultado is None: + resultado = 0 + resultado = round(calcular_operacion(resultado, numero, operador), decimales) + operador = None + + elif resultado is None: + resultado = numero + + else: + mostrar_error(3) + except + + +def main(): + """ + Función principal de la calculadora. Gestiona la entrada del usuario y coordina las operaciones. + + Note: + El flujo del programa es el siguiente: + + 1. Inicia la calculadora mostrando el resultado almacenado por defecto (0.00). + + 2. El usuario ingresa un comando, que puede ser: + - "lista" para ver todas las operaciones disponibles. + - "ce" para reiniciar el resultado almacenado a 0. + - "decimales " para establecer el número de decimales mostrados en el resultado. + - "calculo" para iniciar una secuencia de cálculo paso a paso. + - Una entrada vacía y pulsa la tecla para salir de la calculadora. + + 3. Según el comando ingresado: + - El programa realiza la operación o ejecuta la acción indicada. + - Al ingresar "calculo": + * El usuario es guiado para introducir números y operadores secuencialmente para realizar operaciones básicas. + * El usuario puede utilizar "resultado" en la secuencia de cálculo para reutilizar el resultado almacenado en la calculadora. + * El cálculo finaliza al pulsar , volviendo y actualizando el resultado almacenado de la calculadora con el cálculo realizado. + * También podemos escribir "cancelar", volviendo sin realizar ningún cambio en el resultado almacenado de la calculadora. + + 4. La calculadora sigue ejecutándose hasta que el usuario confirma la salida al ingresar una entrada vacía y pulsar . + + 5. Finalmente, se limpia la pantalla, el programa se despide con el mensaje "\n\nBye, bye...\n\n" y termina. + """ + # TODO: Corrige los errores y haz que el main funcione correctamente... + + decimales = 2 + resultado = 0.0 + + while not desea_salir: + + pedir_entrada(f"Operación (RES => resultado) >> ") + + if entrada == "": + pedir_entrada("¿Desea salir de la calculadora? (s/n) ") + + elif entrada == "lista": + obtener_operaciones + + elif entrada == "ce": + + + elif entrada.startswith("decimales"): + # Extraemos las posiciones decimales y las convertimos a un valor entero + decimales = str(entrada.split()[1]) + print(f"Decimales configurados a {decimales}.") + + elif entrada == "calculo": + realizar_calculo(decimales, resultado) + + else: + mostrar_error diff --git a/dam1_calculadora/cristian_calculadora_alumnos.py b/dam1_calculadora/cristian_calculadora_alumnos.py new file mode 100644 index 0000000..fce6760 --- /dev/null +++ b/dam1_calculadora/cristian_calculadora_alumnos.py @@ -0,0 +1,530 @@ +# TODO: ATENCIÓN!!! +# 1. Importar los paquetes necesarios +# 2. Usar la función mostrar_error para imprimir los errores por consola. +# 3. Los comentarios TODO os ayudan a resolver cada apartado de esta prueba. +# 4. CADA función SOLO puede tener una instrucción return. +# 5. En test_calculadora_alumnos.py crear el test unitario para la función es_resultado_negativo y hacer que todos los test unitarios se cumplan. +import os +import time + + +# Mensajes de error predefinidos +MENSAJES_ERROR = ( + "Problemas al intentar limpiar la pantalla {error}", + "Error al configurar los decimales. Formato: decimales .", + "Entrada no válida. Ingrese número, operador, 'resultado', 'cancelar' o para finalizar el cálculo.", + "Error: Introduzca un operador antes de otro número.", + "Comando no reconocido. Escriba 'lista' para ver las operaciones disponibles.", + "Error: no es posible la división por 0! Introduzca otro valor diferente a 0...", + "Se produjo un error: {error}" +) + +# Operadores soportados por la calculadora +OPERADORES = ( + "+", + "-", + "x", + "*", + "/", + ":", + "**", + "exp" +) +# TODO: Ayuda: incluye en esta constante los símbolos que reconocerá la aplicación para el cálculo de las operaciones: +# '+' para la suma. +# '-' para la resta. +# 'x' o '*' para la multiplicación. +# '/' o ':' para la división. +# '**' o 'exp' para la potencia. + + + +def limpiar_pantalla(): + """ + Limpia la consola según el sistema operativo. + """ + # TODO: El desarrollo de esta función está incompleto y con errores. + try: + os.system("clear" if os.name == "posix" else "cls") + # Otra forma de expresar la misma instrucción sería la siguiente: + # if os.name = posix: + # os.system(clear) + # else: + # os.system(cls) + except Exception as e: + mostrar_error(f"Problemas al intentar limpiar la pantalla: {e}") + + + +def pausa(tecla_enter = False, tiempo = 0): + """ + Pausa la ejecución del programa hasta que se pulse ENTER... "\nPresione ENTER para continuar..." + """ + # TODO: Desarrollar esta función según su documentación. + if tecla_enter: + input("\nPresione ENTER para continuar...") + + elif tiempo > 0: + time.sleep(tiempo) + + + +def mostrar_error(indice_error: int, msj_error = None): + """ + Muestra un mensaje de error en la consola. + + Args: + indice_error (int): Índice del mensaje de error en MENSAJES_ERROR. + msj_error (str, opcional): Texto adicional para personalizar el mensaje de error. + """ + # TODO: + # 1. Corrige el error que existe a la hora de acceder a los mensajes de error que están en la constante + # MENSAJES_ERROR. A los elementos de una tupla se accede igual que a los caracteres de una + # cadena de caracteres. + # 2. Completa el código de esta función para que controle específicamente las excepciones IndexError y + # muestre el mensaje: "\n*ERROR* Mensaje de error no definido.\n" + # 3. También se pide que se controle cualquier otra excepción que se pueda producir y muestre el mensaje: + # "\n*ERROR* Problemas al mostrar error!\n{e}\n" + # 4. En esta función los mensajes de error deben mostrarse con print. + try: + if msj_error != None: + print(f"\n*ERROR* {MENSAJES_ERROR[indice_error].format(error = msj_error)}\n") + + elif IndexError: + print("\n*ERROR* Mensaje de error no definido.\n") + + else: + print(f"\n*ERROR* {MENSAJES_ERROR[indice_error]}\n") + + except Exception as e: + print(f"\n*ERROR* Problemas al mostrar error!\n{e}\n") + + + +def sumar(num1: float, num2: float) -> float: + """ + Realiza la suma de dos números. + + Args: + num1 (float): Primer número. + num2 (float): Segundo número. + + Returns: + float: Resultado de la suma. + """ + # TODO: Realizar el desarrollo completo, incluida la documentación... recibe 2 números float y retorna la suma de ambos. + suma = num1 + num2 + return suma + + + + + +def restar(num1: float, num2: float) -> float: + """ + Realiza la resta de dos números. + + Args: + num1 (float): Primer número. + num2 (float): Segundo número. + + Returns: + float: Resultado de la resta. + """ + # TODO: Realizar el desarrollo completo, incluida la documentación... recibe 2 números float y retorna la resta de ambos. + resta = num1 - num2 + return resta + + + +def es_resultado_negativo(num1: float, num2: float) -> bool: + """ + Determina si el resultado de una operación entre num1 y num2 debe ser negativo. + + Args: + num1 (float): Primer número. + num2 (float): Segundo número. + + Returns: + es_negativo (bool): Dependiendo de si el resultado es negativo o no. + """ + # TODO: Realizar el desarrollo completo de esta función teniendo en cuenta la documentación y completándola también. + # Debe pasar las pruebas unitarias. + # Se trata de una función que os puede venir bien para utilizarla tanto en la función multiplicar, como en dividir. + # Ya que va a determinar si la multiplicación o división entre dos números debería ser de signo negativo o no. + if num1 / num2 >= 0 or num1 * num2 >= 0: + es_negativo = False + + elif num1 / num2 < 0 or num1 * num2 < 0: + es_negativo = True + + return es_negativo + + + +def multiplicar(num1: float, num2: float) -> int: + """ + Realiza la multiplicación ENTERA de dos números usando solo sumas y restas. + + Args: + num1 (float): Primer número. + num2 (float): Segundo número. + + Returns: + int: Resultado de la multiplicación. + + Note: + Debe redondear los números recibidos a enteros para trabajar. + """ + # TODO: + # 1. Realizar el desarrollo completo de esta función teniendo en cuenta la documentación. + # 2. Esta función debe pasar las pruebas unitarias. + # 3. No podéis usar el operador de multiplicación de Python para realizar el desarrollo de la misma, + # es decir, que debéis realizar la multiplicación con SUMAS y/o RESTAS... + # 4. Aunque se reciben números de tipo float, debéis redondearlos como números enteros para + # simplificar esta función, es decir, la operación 4.98 * 3.33, deberá convertirse en 5 * 3. + # 5. Tened en cuenta que podéis recibir números negativos, es decir, la operación -5 * -5 = 25 + # 6. OBLIGATORIO usar un bucle for. + # 7. Incluir algún comentario para mejorar la claridad y permitir que otros comprendan el propósito y funcionamiento del código. + # Redondeamos los números a 0 decimales, como si fueran entero. + num1 = round(num1, 0) + num2 = round(num2, 0) + resultado = 0 + + # Después de hacer compatibles todos los números según lo pedido, hacemos un for donde por cada vez que i obtiene + # un valor, se suma el número. + for i in range(int(abs(num2))): + resultado += int(abs(num1)) + + # Aquí comprobamos si el resultado es negativo o no con la función que se encarga de ello. + if es_resultado_negativo(num1, num2): + resultado = str(resultado) + resultado = "-" + resultado + resultado = int(resultado) + + return resultado + + + +def dividir(num1: float, num2: float) -> int: + """ + Realiza la división ENTERA de dos números usando solo sumas y restas. + + Args: + num1 (float): Dividendo. + num2 (float): Divisor. + + Returns: + int: Resultado de la división. + + Raises: + ZeroDivisionError: Si el divisor es cero. + + Note: + Debe redondear los números recibidos a enteros para trabajar. + """ + # TODO: + # 1. Realizar el desarrollo completo de esta función teniendo en cuenta la documentación. + # 2. Esta función debe pasar las pruebas unitarias. + # 3. No podéis usar el operador de división de Python para realizar el desarrollo de la misma, + # es decir, que debéis realizar la división con SUMAS y/o RESTAS... + # 4. Aunque se reciben números de tipo float, debéis redondearlos como números enteros para + # simplificar esta función, es decir, la operación 4.98 / 3.33, deberá convertirse en 5 / 3. + # 5. Tened en cuenta que podéis recibir números negativos, es decir, la operación -5 / -5 = 1 + # 6. Incluir algún comentario para mejorar la claridad y permitir que otros comprendan el propósito y funcionamiento del código. + # Redondeamos los números a 0 decimales, como si fueran entero. + num1 = round(num1, 0) + num2 = round(num2, 0) + resultado = 0.0 + + contador = int(abs(num1)) + # Si el divisor es 0, muestra el error 5. + if num2 == 0: + raise ZeroDivisionError(mostrar_error(5)) + # Este while va controlando que el dividendo sea mayor que el divisor y así restando el número tantas veces como haga falta. + else: + while contador >= abs(int(num2)): + contador -= abs(int(num1)) + resultado += 1 + # Aquí comprobamos si el resultado es negativo o no con la función que se encarga de ello. + if es_resultado_negativo(num1, num2): + resultado = str(resultado) + resultado = "-" + resultado + resultado = int(resultado) + + return resultado + + + + +def potencia(): + """ + Esta función recoge dos números y hace que el segundo sea la potencia del primero. + + Args: + num1 (float) = Base + num2 (float) = Exponente + + Returns: + resultado (int) = Resultado de la operación. + """ + # TODO: + # 1. Realizar el desarrollo completo de esta función y su documentación. + # 2. Esta función debe pasar las pruebas unitarias. + # 3. PREMISAS a tener en cuenta: + # - Cualquier número elevado a 0 da como resultado 1. + # - Para simplificar esta función, vamos a suponer que un número elevado a un + # exponente negativo siempre dará 0 (aunque en realidad no es así matemáticamente) + # 4. Utiliza la función de multiplicar para realizar los cálculos que te + # harán falta en esta función (RECUERDA que no puedes usar directamente los operadores + # de Python para la multiplicación y división). + # 5. Incluir algún comentario para mejorar la claridad y permitir que otros comprendan el propósito y funcionamiento del código. + num1 = round(num1, 0) + num2 = round(num2, 0) + resultado = 0 + + # Aquí comprobamos si el exponente es 0 o negativo porque el resultado será siempre 1 o 0 dependiendo el caso. + if num2 == 0: + resultado = 1 + elif num2 < 0: + resultado = 0 + + + return resultado + + + + +def pedir_entrada(msj: str) -> str: + """ + Pide al usuario una entrada, elimina espacios por delante y por detrás y la convierte a minúsculas. + + Args: + msj (str): Mensaje para solicitar la entrada. + + Returns: + str: Entrada del usuario. + """ + # TODO: El desarrollo de esta función está incompleto, debéis terminarla teniendo en cuenta la documentación + return input(msj).lower().strip() + + +def calcular_operacion(num1: float, num2: float, operador: str) -> float: + """ + Realiza la operación especificada entre num1 y num2 dependiendo del valor del operador. + + Args: + num1 (float): Primer número. + num2 (float): Segundo número. + operador (str): Operador de la operación. + + Returns: + float: Resultado de la operación. + + Raises: + ZeroDivisionError: Si el divisor es cero. + """ + # TODO: El desarrollo de esta función está incompleto... completadla teniendo en cuenta la documentación + # y que debe realizar las llamadas adecuadas a las funciones ya creadas para realizar los distintos + # cálculos... sumar, restar, multiplicar, dividir y potencia. + # IMPORTANTE: Si hacemos caso a la documentación de esta función, NO debéis capturar la excepción + # ZeroDivisionError aquí, sino que hay que dejadla que se propague a la función llamante, realizar_calculo(), + # dónde se os indica cómo realizar la gestión de estos errores. + resultado = 0 + + if operador == "+": + resultado = sumar(num1, num2) + + elif operador == "-": + resultado = restar(num1, num2) + + elif operador == "x" or operador == "*": + resultado = multiplicar(num1, num2) + + elif operador == "/" or operador == ":": + if num2 != 0.0: + resultado = dividir(num1, num2) + else: + mostrar_error(5) + + elif operador == "**" or operador == "exp": + resultado = potencia(num1, num2) + + return resultado + + + +def obtener_operaciones() -> str: + """ + Devuelve una cadena con la lista de operaciones disponibles en la calculadora. + + Returns: + (str): cadena de caracteres con la información de las operaciones disponibles. + """ + # TODO: El desarrollo de esta función está incompleto... ver documentación para solucionarla correctamente. + """ + Operaciones disponibles: + ce => Reiniciar resultado a 0 + decimales => Establecer decimales en resultado + cadena vacía + => Pregunta si desea salir + calculo => Iniciar cálculo secuencial + + => Suma + - => Resta + x o * => Multiplicación + / o : => División + ** o exp => Potencia + cancelar => volver sin actualizar resultado de la calculadora + cadena vacía + => volver actualizando resultado de la calculadora + """ + lista = "Operaciones disponibles:\nce => Reiniciar resultado a 0\ndecimales => Establecer decimales en resultado\ncadena vacía + => Pregunta si desea salir\ncalculo => Iniciar cálculo secuencial\n+ => Suma\n- => Resta\nx o * => Multiplicación\n/ o : => División\n** o exp => Potencia\ncancelar => volver sin actualizar resultado de la calculadora\ncadena vacía + => volver actualizando resultado de la calculadora" + # Ya no sé ni lo que hago Diego. + return lista + + +def realizar_calculo(decimales: int, resultado_almacenado: int) -> float: + """ + Realiza una secuencia de cálculos solicitando números y operadores al usuario. + + Args: + decimales (int): Número de decimales para el resultado. + resultado_almacenado (float): Valor almacenado en la calculadora. + + Returns: + float: Resultado final del cálculo o None si se cancela. + + Note: + * Dentro de esta función el usuario puede realizar cálculos secuenciales, es decir, + comenzará introduciendo un número, después un operador, y otro número... a partir + de aquí sobre el resultado acumulado, introducirá operador y número para seguir + realizando cálculos (ver ejemplos en README.md de la tarea en el repositorio de GitHub). + * El usuario es guiado para introducir números y operadores secuencialmente + para realizar operaciones básicas. + * El usuario puede utilizar "resultado" en la secuencia de cálculo para reutilizar el + resultado almacenado en la calculadora. + * El cálculo finaliza al pulsar , volviendo y actualizando el resultado almacenado + de la calculadora con el cálculo realizado. + * También podemos escribir "cancelar", volviendo sin realizar ningún cambio en el + resultado almacenado de la calculadora. + """ + # TODO: El desarrollo de esta función está incompleto... ver documentación para solucionarla correctamente. + + operador = None + resultado = None + realizando_calculos = True + + print("\n## Ingrese número, operador, 'resultado', 'cancelar' o para finalizar el cálculo ##\n") + + while realizando_calculos: + entrada = pedir_entrada(f"\t (Cálculo = {resultado}) >> ") + + if entrada == "cancelar": + resultado_almacenado = None + realizando_calculos = False + + + elif entrada == "": + resultado = resultado_almacenado + + elif entrada in OPERADORES: + operador = entrada + + else: + # TODO: este código funciona cuando solucionéis que reconozca las variables resultado_almacenado y decimales. + # Pero no gestiona los posibles tipos de Excepciones que se pueden producir: + # - ValueError que debe mostrar el error que está en la posición 2 de MENSAJES_ERROR. + # - ZeroDivisionError que debe mostrar el error que está en la posición 5 de MENSAJES_ERROR. + # - Exception que debe mostrar el error que está en la posición 6 de MENSAJES_ERROR. + if entrada == "resultado": + entrada = resultado_almacenado + + try: + numero = float(entrada) + + if operador is not None: + if resultado is None: + resultado = 0 + resultado = round(calcular_operacion(resultado, numero, operador), decimales) + operador = None + + elif resultado is None: + resultado = numero + + else: + mostrar_error(3) + except ValueError: + mostrar_error(2) + except ZeroDivisionError: + mostrar_error(5) + except Exception: + mostrar_error(6) + + return resultado_almacenado + + +def main(): + """ + Función principal de la calculadora. Gestiona la entrada del usuario y coordina las operaciones. + + Note: + El flujo del programa es el siguiente: + + 1. Inicia la calculadora mostrando el resultado almacenado por defecto (0.00). + + 2. El usuario ingresa un comando, que puede ser: + - "lista" para ver todas las operaciones disponibles. + - "ce" para reiniciar el resultado almacenado a 0. + - "decimales " para establecer el número de decimales mostrados en el resultado. + - "calculo" para iniciar una secuencia de cálculo paso a paso. + - Una entrada vacía y pulsa la tecla para salir de la calculadora. + + 3. Según el comando ingresado: + - El programa realiza la operación o ejecuta la acción indicada. + - Al ingresar "calculo": + * El usuario es guiado para introducir números y operadores secuencialmente para realizar operaciones básicas. + * El usuario puede utilizar "resultado" en la secuencia de cálculo para reutilizar el resultado almacenado en la calculadora. + * El cálculo finaliza al pulsar , volviendo y actualizando el resultado almacenado de la calculadora con el cálculo realizado. + * También podemos escribir "cancelar", volviendo sin realizar ningún cambio en el resultado almacenado de la calculadora. + + 4. La calculadora sigue ejecutándose hasta que el usuario confirma la salida al ingresar una entrada vacía y pulsar . + + 5. Finalmente, se limpia la pantalla, el programa se despide con el mensaje "\n\nBye, bye...\n\n" y termina. + """ + # TODO: Corrige los errores y haz que el main funcione correctamente... + + decimales = 2 + resultado = 0.0 + desea_salir = False + + while not desea_salir: + + entrada = pedir_entrada(f"Operación (RES => {resultado}) >> ") + + if entrada == "": + entrada = pedir_entrada("¿Desea salir de la calculadora? (s/n) ") + if entrada == "s": + limpiar_pantalla() + print("\n\nBye, bye...\n\n") + break + + + elif entrada == "lista": + obtener_operaciones() + + elif entrada == "ce": + resultado = 0.0 + + elif entrada.startswith("decimales"): + # Extraemos las posiciones decimales y las convertimos a un valor entero + decimales = str(entrada.split()[1]) + print(f"Decimales configurados a {decimales}.") + resultado = round(resultado, int(decimales)) + + elif entrada == "calculo": + realizar_calculo(decimales, resultado) + + else: + mostrar_error(4) + + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/dam1_calculadora/curro_calculadora_alumnos.py b/dam1_calculadora/curro_calculadora_alumnos.py new file mode 100644 index 0000000..573e0e5 --- /dev/null +++ b/dam1_calculadora/curro_calculadora_alumnos.py @@ -0,0 +1,350 @@ +# TODO: ATENCIÓN!!! +# 1. Importar los paquetes necesarios +# 2. Usar la función mostrar_error para imprimir los errores por consola. +# 3. Los comentarios TODO os ayudan a resolver cada apartado de esta prueba. +# 4. CADA función SOLO puede tener una instrucción return. +# 5. En test_calculadora_alumnos.py crear el test unitario para la función es_resultado_negativo y hacer que todos los test unitarios se cumplan. + + + +# Mensajes de error predefinidos +MENSAJES_ERROR = ( + "Problemas al intentar limpiar la pantalla {error}", + "Error al configurar los decimales. Formato: decimales .", + "Entrada no válida. Ingrese número, operador, 'resultado', 'cancelar' o para finalizar el cálculo.", + "Error: Introduzca un operador antes de otro número.", + "Comando no reconocido. Escriba 'lista' para ver las operaciones disponibles.", + "Error: no es posible la división por 0! Introduzca otro valor diferente a 0...", + "Se produjo un error: {error}" +) + +# Operadores soportados por la calculadora +OPERADORES = +# TODO: Ayuda: incluye en esta constante los símbolos que reconocerá la aplicación para el cálculo de las operaciones: +# '+' para la suma. +# '-' para la resta. +# 'x' o '*' para la multiplicación. +# '/' o ':' para la división. +# '**' o 'exp' para la potencia. + +import os + +def limpiar_pantalla(): + """ + Limpia la consola según el sistema operativo. + """ + # TODO: El desarrollo de esta función está incompleto y con errores. + if os.name == 'nt': # Este para Windows. + os.system('cls') + else: # Este será para macOS y Linux. + os.system('clear') + + + +def pausa(): + """ + Pausa la ejecución del programa hasta que se pulse ENTER... "\nPresione ENTER para continuar..." + """ + +def mostrar_error(indice_error: int, msj_error = None): + """ + Muestra un mensaje de error en la consola. + + Args: + indice_error (int): Índice del mensaje de error en MENSAJES_ERROR. + msj_error (str, opcional): Texto adicional para personalizar el mensaje de error. + """ + # TODO: + # 1. Corrige el error que existe a la hora de acceder a los mensajes de error que están en la constante + # MENSAJES_ERROR. A los elementos de una tupla se accede igual que a los caracteres de una + # cadena de caracteres. + # 2. Completa el código de esta función para que controle específicamente las excepciones IndexError y + # muestre el mensaje: "\n*ERROR* Mensaje de error no definido.\n" + # 3. También se pide que se controle cualquier otra excepción que se pueda producir y muestre el mensaje: + # "\n*ERROR* Problemas al mostrar error!\n{e}\n" + # 4. En esta función los mensajes de error deben mostrarse con print. + if msj_error != None: + print(f"\n*ERROR* {MENSAJES_ERROR.format(error = msj_error)}\n") + else: + print(f"\n*ERROR* {MENSAJES_ERROR}\n") + + +def sumar(): + # TODO: Realizar el desarrollo completo, incluida la documentación... recibe 2 números float y retorna la suma de ambos. + + + +def restar(): + # TODO: Realizar el desarrollo completo, incluida la documentación... recibe 2 números float y retorna la resta de ambos. + + + +def es_resultado_negativo(num1: float, num2: float) -> bool: + """Determina si el resultado de una operación entre num1 y num2 debe ser negativo.""" + # TODO: Realizar el desarrollo completo de esta función teniendo en cuenta la documentación y completándola también. + # Debe pasar las pruebas unitarias. + # Se trata de una función que os puede venir bien para utilizarla tanto en la función multiplicar, como en dividir. + # Ya que va a determinar si la multiplicación o división entre dos números debería ser de signo negativo o no. + + + +def multiplicar(): + """ + Realiza la multiplicación ENTERA de dos números usando solo sumas y restas. + + Args: + num1 (float): Primer número. + num2 (float): Segundo número. + + Returns: + int: Resultado de la multiplicación. + + Note: + Debe redondear los números recibidos a enteros para trabajar. + """ + # TODO: + # 1. Realizar el desarrollo completo de esta función teniendo en cuenta la documentación. + # 2. Esta función debe pasar las pruebas unitarias. + # 3. No podéis usar el operador de multiplicación de Python para realizar el desarrollo de la misma, + # es decir, que debéis realizar la multiplicación con SUMAS y/o RESTAS... + # 4. Aunque se reciben números de tipo float, debéis redondearlos como números enteros para + # simplificar esta función, es decir, la operación 4.98 * 3.33, deberá convertirse en 5 * 3. + # 5. Tened en cuenta que podéis recibir números negativos, es decir, la operación -5 * -5 = 25 + # 6. OBLIGATORIO usar un bucle for. + # 7. Incluir algún comentario para mejorar la claridad y permitir que otros comprendan el propósito y funcionamiento del código. + + + +def dividir(): + """ + Realiza la división ENTERA de dos números usando solo sumas y restas. + + Args: + num1 (float): Dividendo. + num2 (float): Divisor. + + Returns: + int: Resultado de la división. + + Raises: + ZeroDivisionError: Si el divisor es cero. + + Note: + Debe redondear los números recibidos a enteros para trabajar. + """ + # TODO: + # 1. Realizar el desarrollo completo de esta función teniendo en cuenta la documentación. + # 2. Esta función debe pasar las pruebas unitarias. + # 3. No podéis usar el operador de división de Python para realizar el desarrollo de la misma, + # es decir, que debéis realizar la división con SUMAS y/o RESTAS... + # 4. Aunque se reciben números de tipo float, debéis redondearlos como números enteros para + # simplificar esta función, es decir, la operación 4.98 / 3.33, deberá convertirse en 5 / 3. + # 5. Tened en cuenta que podéis recibir números negativos, es decir, la operación -5 / -5 = 1 + # 6. Incluir algún comentario para mejorar la claridad y permitir que otros comprendan el propósito y funcionamiento del código. + + +def potencia(): + # TODO: + # 1. Realizar el desarrollo completo de esta función y su documentación. + # 2. Esta función debe pasar las pruebas unitarias. + # 3. PREMISAS a tener en cuenta: + # - Cualquier número elevado a 0 da como resultado 1. + # - Para simplificar esta función, vamos a suponer que un número elevado a un + # exponente negativo siempre dará 0 (aunque en realidad no es así matemáticamente) + # 4. Utiliza la función de multiplicar para realizar los cálculos que te + # harán falta en esta función (RECUERDA que no puedes usar directamente los operadores + # de Python para la multiplicación y división). + # 5. Incluir algún comentario para mejorar la claridad y permitir que otros comprendan el propósito y funcionamiento del código. + + +def pedir_entrada(msj: str) -> str: + """ + Pide al usuario una entrada, elimina espacios por delante y por detrás y la convierte a minúsculas. + + Args: + msj (str): Mensaje para solicitar la entrada. + + Returns: + str: Entrada del usuario. + """ + # TODO: El desarrollo de esta función está incompleto, debéis terminarla teniendo en cuenta la documentación + return input(msj) + + +def calcular_operacion(num1: float, num2: float, operador: str) -> float: + """ + Realiza la operación especificada entre num1 y num2 dependiendo del valor del operador. + + Args: + num1 (float): Primer número. + num2 (float): Segundo número. + operador (str): Operador de la operación. + + Returns: + float: Resultado de la operación. + + Raises: + ZeroDivisionError: Si el divisor es cero. + """ + # TODO: El desarrollo de esta función está incompleto... completadla teniendo en cuenta la documentación + # y que debe realizar las llamadas adecuadas a las funciones ya creadas para realizar los distintos + # cálculos... sumar, restar, multiplicar, dividir y potencia. + # IMPORTANTE: Si hacemos caso a la documentación de esta función, NO debéis capturar la excepción + # ZeroDivisionError aquí, sino que hay que dejadla que se propague a la función llamante, realizar_calculo(), + # dónde se os indica cómo realizar la gestión de estos errores. + + return resultado + + + +def obtener_operaciones() -> str: + """ + Devuelve una cadena con la lista de operaciones disponibles en la calculadora. + + Returns: + (str): cadena de caracteres con la información de las operaciones disponibles. + """ + # TODO: El desarrollo de esta función está incompleto... ver documentación para solucionarla correctamente. + """ + Operaciones disponibles: + ce => Reiniciar resultado a 0 + decimales => Establecer decimales en resultado + cadena vacía + => Pregunta si desea salir + calculo => Iniciar cálculo secuencial + + => Suma + - => Resta + x o * => Multiplicación + / o : => División + ** o exp => Potencia + cancelar => vovler sin actualizar resultado de la calculadora + cadena vacía + => volver actualizando resultado de la calculadora + """ + + +def realizar_calculo(): + """ + Realiza una secuencia de cálculos solicitando números y operadores al usuario. + + Args: + decimales (int): Número de decimales para el resultado. + resultado_almacenado (float): Valor almacenado en la calculadora. + + Returns: + float: Resultado final del cálculo o None si se cancela. + + Note: + * Dentro de esta función el usuario puede realizar cálculos secuenciales, es decir, + comenzará introduciendo un número, después un operador, y otro número... a partir + de aquí sobre el resultado acumulado, introducirá operador y número para seguir + realizando cálculos (ver ejemplos en README.md de la tarea en el repositorio de GitHub). + * El usuario es guiado para introducir números y operadores secuencialmente + para realizar operaciones básicas. + * El usuario puede utilizar "resultado" en la secuencia de cálculo para reutilizar el + resultado almacenado en la calculadora. + * El cálculo finaliza al pulsar , volviendo y actualizando el resultado almacenado + de la calculadora con el cálculo realizado. + * También podemos escribir "cancelar", volviendo sin realizar ningún cambio en el + resultado almacenado de la calculadora. + """ + # TODO: El desarrollo de esta función está incompleto... ver documentación para solucionarla correctamente. + + operador = None + resultado = None + realizando_calculos = True + + print("\n## Ingrese número, operador, 'resultado', 'cancelar' o para finalizar el cálculo ##\n") + + while realizando_calculos: + entrada = pedir_entrada(f"\t (Cálculo = resultado) >> ") + + if entrada == "cancelar": + + + elif entrada == "": + + + elif entrada in OPERADORES: + operador = + + else: + # TODO: este código funciona cuando solucionéis que reconozca las variables resultado_almacenado y decimales. + # Pero no gestiona los posibles tipos de Excepciones que se pueden producir: + # - ValueError que debe mostrar el error que está en la posición 2 de MENSAJES_ERROR. + # - ZeroDivisionError que debe mostrar el error que está en la posición 5 de MENSAJES_ERROR. + # - Exception que debe mostrar el error que está en la posición 6 de MENSAJES_ERROR. + if entrada == "resultado": + entrada = resultado_almacenado + + try: + numero = float(entrada) + + if operador is not None: + if resultado is None: + resultado = 0 + resultado = round(calcular_operacion(resultado, numero, operador), decimales) + operador = None + + elif resultado is None: + resultado = numero + + else: + mostrar_error(3) + except + + +def main(): + """ + Función principal de la calculadora. Gestiona la entrada del usuario y coordina las operaciones. + + Note: + El flujo del programa es el siguiente: + + 1. Inicia la calculadora mostrando el resultado almacenado por defecto (0.00). + + 2. El usuario ingresa un comando, que puede ser: + - "lista" para ver todas las operaciones disponibles. + - "ce" para reiniciar el resultado almacenado a 0. + - "decimales " para establecer el número de decimales mostrados en el resultado. + - "calculo" para iniciar una secuencia de cálculo paso a paso. + - Una entrada vacía y pulsa la tecla para salir de la calculadora. + + 3. Según el comando ingresado: + - El programa realiza la operación o ejecuta la acción indicada. + - Al ingresar "calculo": + * El usuario es guiado para introducir números y operadores secuencialmente para realizar operaciones básicas. + * El usuario puede utilizar "resultado" en la secuencia de cálculo para reutilizar el resultado almacenado en la calculadora. + * El cálculo finaliza al pulsar , volviendo y actualizando el resultado almacenado de la calculadora con el cálculo realizado. + * También podemos escribir "cancelar", volviendo sin realizar ningún cambio en el resultado almacenado de la calculadora. + + 4. La calculadora sigue ejecutándose hasta que el usuario confirma la salida al ingresar una entrada vacía y pulsar . + + 5. Finalmente, se limpia la pantalla, el programa se despide con el mensaje "\n\nBye, bye...\n\n" y termina. + """ + # TODO: Corrige los errores y haz que el main funcione correctamente... + + decimales = 2 + resultado = 0.0 + + while not desea_salir: + + pedir_entrada(f"Operación (RES => resultado) >> ") + + if entrada == "": + pedir_entrada("¿Desea salir de la calculadora? (s/n) ") + + elif entrada == "lista": + obtener_operaciones + + elif entrada == "ce": + + + elif entrada.startswith("decimales"): + # Extraemos las posiciones decimales y las convertimos a un valor entero + decimales = str(entrada.split()[1]) + print(f"Decimales configurados a {decimales}.") + + elif entrada == "calculo": + realizar_calculo(decimales, resultado) + + else: + mostrar_error diff --git a/dam1_calculadora/elvira_calculadora_alumnos.py b/dam1_calculadora/elvira_calculadora_alumnos.py new file mode 100644 index 0000000..e150576 --- /dev/null +++ b/dam1_calculadora/elvira_calculadora_alumnos.py @@ -0,0 +1,468 @@ +# TODO: ATENCIÓN!!! +# 1. Importar los paquetes necesarios +# 2. Usar la función mostrar_error para imprimir los errores por consola. +# 3. Los comentarios TODO os ayudan a resolver cada apartado de esta prueba. +# 4. CADA función SOLO puede tener una instrucción return. +# 5. En test_calculadora_alumnos.py crear el test unitario para la función es_resultado_negativo y hacer que todos los test unitarios se cumplan. + + + +# Mensajes de error predefinidos +import os + + +MENSAJES_ERROR = ( + "Problemas al intentar limpiar la pantalla {error}", + "Error al configurar los decimales. Formato: decimales .", + "Entrada no válida. Ingrese número, operador, 'resultado', 'cancelar' o para finalizar el cálculo.", + "Error: Introduzca un operador antes de otro número.", + "Comando no reconocido. Escriba 'lista' para ver las operaciones disponibles.", + "Error: no es posible la división por 0! Introduzca otro valor diferente a 0...", + "Se produjo un error: {error}" +) + +# Operadores soportados por la calculadora +OPERADORES = "+", "-", "x", "*", "/", ":", "**", "exp" +# TODO: Ayuda: incluye en esta constante los símbolos que reconocerá la aplicación para el cálculo de las operaciones: +# '+' para la suma. +# '-' para la resta. +# 'x' o '*' para la multiplicación. +# '/' o ':' para la división. +# '**' o 'exp' para la potencia. + + + +def limpiar_pantalla(): + """ + Limpia la consola según el sistema operativo. + """ + # TODO: El desarrollo de esta función está incompleto y con errores. + try: + os.system('clear' if os.name == "posix" else 'cls') + # Otra forma de expresar la misma instrucción sería la siguiente: + # if os.name = posix: + # os.system(clear) + # else: + # os.system(cls) + except Exception as e: + mostrar_error(f"Problemas al intentar limpiar la pantalla: {e}") + + + +def pausa(tecla_enter = False): + + if tecla_enter: + input ("\n Presione enter para continuar...") + + + """ + Pausa la ejecución del programa hasta que se pulse ENTER... "\nPresione ENTER para continuar..." + """ + # TODO: Desarrollar esta función según su documentación. + + + + +def mostrar_error(indice_error: int, msj_error = None): + + """ + Muestra un mensaje de error en la consola. + + Args: + indice_error (int): Índice del mensaje de error en MENSAJES_ERROR. + msj_error (str, opcional): Texto adicional para personalizar el mensaje de error. + """ + # TODO: + # 1. Corrige el error que existe a la hora de acceder a los mensajes de error que están en la constante + # MENSAJES_ERROR. A los elementos de una tupla se accede igual que a los caracteres de una + # cadena de caracteres. + # 2. Completa el código de esta función para que controle específicamente las excepciones IndexError y + # muestre el mensaje: "\n*ERROR* Mensaje de error no definido.\n" + # 3. También se pide que se controle cualquier otra excepción que se pueda producir y muestre el mensaje: + # "\n*ERROR* Problemas al mostrar error!\n{e}\n" + # 4. En esta función los mensajes de error deben mostrarse con print. + try: + msj_error != None + + except IndexError as e: + print(f"\n*ERROR* Mensaje de error no definido.\n") + + + except: + print(f"\n*ERROR* {MENSAJES_ERROR.format(error = msj_error)}\n") + print(f"\n*ERROR* {MENSAJES_ERROR}\n") + + "Problemas al intentar limpiar la pantalla {error}", + "Error al configurar los decimales. Formato: decimales .", + "Entrada no válida. Ingrese número, operador, 'resultado', 'cancelar' o para finalizar el cálculo.", + "Error: Introduzca un operador antes de otro número.", + "Comando no reconocido. Escriba 'lista' para ver las operaciones disponibles.", + "Error: no es posible la división por 0! Introduzca otro valor diferente a 0...", + "Se produjo un error: {error}" + + +def sumar(num1: float, num2: float) -> float: + """Hace la suma entre num1 y num 2 + + Args: + num1 (float): Primer número. + num2 (float): Segundo número. + + Returns: + resultado (float): Resultado de la suma. + """ + + # TODO: Realizar el desarrollo completo, incluida la documentación... recibe 2 números float y retorna la suma de ambos. + + resultado = num1 + num2 + return resultado + + +def restar(num1: float, num2: float) -> float: + # TODO: Realizar el desarrollo completo, incluida la documentación... recibe 2 números float y retorna la resta de ambos. + resultado = num1 - num2 + return resultado + + +def es_resultado_negativo(num1: float, num2: float) -> bool: + """Determina si el resultado de una operación entre num1 y num2 debe ser negativo.""" + # TODO: Realizar el desarrollo completo de esta función teniendo en cuenta la documentación y completándola también. + # Debe pasar las pruebas unitarias. + # Se trata de una función que os puede venir bien para utilizarla tanto en la función multiplicar, como en dividir. + # Ya que va a determinar si la multiplicación o división entre dos números debería ser de signo negativo o no. + es_negativo = False + + if num2 > num1: + es_negativo = True + + return es_negativo + + + +def multiplicar(num1: float, num2: float) -> int: + """ + Realiza la multiplicación ENTERA de dos números usando solo sumas y restas. + + Args: + num1 (float): Primer número. + num2 (float): Segundo número. + + Returns: + int: Resultado de la multiplicación. + + Note: + Debe redondear los números recibidos a enteros para trabajar. + """ + # TODO: + # 1. Realizar el desarrollo completo de esta función teniendo en cuenta la documentación. + # 2. Esta función debe pasar las pruebas unitarias. + # 3. No podéis usar el operador de multiplicación de Python para realizar el desarrollo de la misma, + # es decir, que debéis realizar la multiplicación con SUMAS y/o RESTAS... + # 4. Aunque se reciben números de tipo float, debéis redondearlos como números enteros para + # simplificar esta función, es decir, la operación 4.98 * 3.33, deberá convertirse en 5 * 3. + # 5. Tened en cuenta que podéis recibir números negativos, es decir, la operación -5 * -5 = 25 + # 6. OBLIGATORIO usar un bucle for. + # 7. Incluir algún comentario para mejorar la claridad y permitir que otros comprendan el propósito y funcionamiento del código. + + resultado = 0 + + for i in range (1,num1 + 1): + resultado += num2 + + return resultado + + +def dividir(): + """ + Realiza la división ENTERA de dos números usando solo sumas y restas. + + Args: + num1 (float): Dividendo. + num2 (float): Divisor. + + Returns: + int: Resultado de la división. + + Raises: + ZeroDivisionError: Si el divisor es cero. + + Note: + Debe redondear los números recibidos a enteros para trabajar. + """ + # TODO: + # 1. Realizar el desarrollo completo de esta función teniendo en cuenta la documentación. + # 2. Esta función debe pasar las pruebas unitarias. + # 3. No podéis usar el operador de división de Python para realizar el desarrollo de la misma, + # es decir, que debéis realizar la división con SUMAS y/o RESTAS... + # 4. Aunque se reciben números de tipo float, debéis redondearlos como números enteros para + # simplificar esta función, es decir, la operación 4.98 / 3.33, deberá convertirse en 5 / 3. + # 5. Tened en cuenta que podéis recibir números negativos, es decir, la operación -5 / -5 = 1 + # 6. Incluir algún comentario para mejorar la claridad y permitir que otros comprendan el propósito y funcionamiento del código. + + +def potencia(num1: float, num2: float) -> float: + # TODO: + # 1. Realizar el desarrollo completo de esta función y su documentación. + # 2. Esta función debe pasar las pruebas unitarias. + # 3. PREMISAS a tener en cuenta: + # - Cualquier número elevado a 0 da como resultado 1. + # - Para simplificar esta función, vamos a suponer que un número elevado a un + # exponente negativo siempre dará 0 (aunque en realidad no es así matemáticamente) + # 4. Utiliza la función de multiplicar para realizar los cálculos que te + # harán falta en esta función (RECUERDA que no puedes usar directamente los operadores + # de Python para la multiplicación y división). + # 5. Incluir algún comentario para mejorar la claridad y permitir que otros comprendan el propósito y funcionamiento del código. + if num2 == 0: + resultado = 1 + elif num2 <0: + resultado = 0 + else: + resultado = multiplicar(num1,num2) + + +def pedir_entrada(msj: str) -> str: + + entrada = input(msj).strip().lower() + """ + Pide al usuario una entrada, elimina espacios por delante y por detrás y la convierte a minúsculas. + + Args: + msj (str): Mensaje para solicitar la entrada. + + Returns: + str: Entrada del usuario. + """ + # TODO: El desarrollo de esta función está incompleto, debéis terminarla teniendo en cuenta la documentación + + return entrada + + +def calcular_operacion(num1: float, num2: float, operador: str) -> float: + """ + Realiza la operación especificada entre num1 y num2 dependiendo del valor del operador. + + Args: + num1 (float): Primer número. + num2 (float): Segundo número. + operador (str): Operador de la operación. + + Returns: + float: Resultado de la operación. + + Raises: + ZeroDivisionError: Si el divisor es cero. + """ + # TODO: El desarrollo de esta función está incompleto... completadla teniendo en cuenta la documentación + # y que debe realizar las llamadas adecuadas a las funciones ya creadas para realizar los distintos + # cálculos... sumar, restar, multiplicar, dividir y potencia. + # IMPORTANTE: Si hacemos caso a la documentación de esta función, NO debéis capturar la excepción + # ZeroDivisionError aquí, sino que hay que dejadla que se propague a la función llamante, realizar_calculo(), + # dónde se os indica cómo realizar la gestión de estos errores. + + try: + operador in OPERADORES + if operador == "+": + resultado = sumar(num1,num2) + + elif operador == "-": + resultado = restar(num1,num2) + + elif operador == "*": + resultado = multiplicar(num1,num2) + + elif operador == "/" or operador == ":": + resultado = dividir(num1,num2) + + else: + resultado = potencia(num1,num2) + + except ValueError as e: + mostrar_error(f"Entrada no válidad{e}") + + + + + return resultado + + + +def obtener_operaciones() -> str: + """ + Devuelve una cadena con la lista de operaciones disponibles en la calculadora. + + Returns: + (str): cadena de caracteres con la información de las operaciones disponibles. + """ + # TODO: El desarrollo de esta función está incompleto... ver documentación para solucionarla correctamente. + """ + Operaciones disponibles: + ce => Reiniciar resultado a 0 + decimales => Establecer decimales en resultado + cadena vacía + => Pregunta si desea salir + calculo => Iniciar cálculo secuencial + + => Suma + - => Resta + x o * => Multiplicación + / o : => División + ** o exp => Potencia + cancelar => vovler sin actualizar resultado de la calculadora + cadena vacía + => volver actualizando resultado de la calculadora + """ + + return("\nOperaciones disponibles:\n ce => Reiniciar resultado a 0\n decimales => Establecer decimales en resultado\n cadena vacía + => Pregunta si desea salir\n calculo => Iniciar cálculo secuencial\n + => Suma\n - => Resta\n x o * => Multiplicación\n / o : => División \n ** o exp => Potencia\n cancelar => vovler sin actualizar resultado de la calculadora\n cadena vacía + => volver actualizando resultado de la calculadora") + + +def realizar_calculo(decimales,resultado_almacenado,num1,num2): + """ + Realiza una secuencia de cálculos solicitando números y operadores al usuario. + + Args: + decimales (int): Número de decimales para el resultado. + resultado_almacenado (float): Valor almacenado en la calculadora. + + Returns: + float: Resultado final del cálculo o None si se cancela. + + Note: + * Dentro de esta función el usuario puede realizar cálculos secuenciales, es decir, + comenzará introduciendo un número, después un operador, y otro número... a partir + de aquí sobre el resultado acumulado, introducirá operador y número para seguir + realizando cálculos (ver ejemplos en README.md de la tarea en el repositorio de GitHub). + * El usuario es guiado para introducir números y operadores secuencialmente + para realizar operaciones básicas. + * El usuario puede utilizar "resultado" en la secuencia de cálculo para reutilizar el + resultado almacenado en la calculadora. + * El cálculo finaliza al pulsar , volviendo y actualizando el resultado almacenado + de la calculadora con el cálculo realizado. + * También podemos escribir "cancelar", volviendo sin realizar ningún cambio en el + resultado almacenado de la calculadora. + """ + # TODO: El desarrollo de esta función está incompleto... ver documentación para solucionarla correctamente. + + operador = None + resultado = None + realizando_calculos = True + + print("\n## Ingrese número, operador, 'resultado', 'cancelar' o para finalizar el cálculo ##\n") + + while realizando_calculos: + entrada= pedir_entrada(f"\t (Cálculo = resultado) >> ") + + + if entrada == "cancelar": + realizando_calculos = False + return resultado, f"Cálculo cancelado, resultado no modificado (RES => {resultado:.{decimales}f})." + + + elif entrada == "": + resultado_almacenado += resultado + + + + elif entrada in OPERADORES: + + operador = calcular_operacion(num1, num2, operador) + + + else: + # TODO: este código funciona cuando solucionéis que reconozca las variables resultado_almacenado y decimales. + # Pero no gestiona los posibles tipos de Excepciones que se pueden producir: + # - ValueError que debe mostrar el error que está en la posición 2 de MENSAJES_ERROR. + # - ZeroDivisionError que debe mostrar el error que está en la posición 5 de MENSAJES_ERROR. + # - Exception que debe mostrar el error que está en la posición 6 de MENSAJES_ERROR. + if entrada == "resultado": + entrada = resultado_almacenado + + try: + numero = float(entrada) + + if operador is not None: + if resultado is None: + resultado = 0 + resultado = round(calcular_operacion(resultado, numero, operador), decimales) + operador = None + + elif resultado is None: + resultado = numero + + else: + mostrar_error(3) + except ValueError as e: + mostrar_error(f"Entrada no válidad{e}") + + + +def main(): + """ + Función principal de la calculadora. Gestiona la entrada del usuario y coordina las operaciones. + + Note: + El flujo del programa es el siguiente: + + 1. Inicia la calculadora mostrando el resultado almacenado por defecto (0.00). + + 2. El usuario ingresa un comando, que puede ser: + - "lista" para ver todas las operaciones disponibles. + - "ce" para reiniciar el resultado almacenado a 0. + - "decimales " para establecer el número de decimales mostrados en el resultado. + - "calculo" para iniciar una secuencia de cálculo paso a paso. + - Una entrada vacía y pulsa la tecla para salir de la calculadora. + + 3. Según el comando ingresado: + - El programa realiza la operación o ejecuta la acción indicada. + - Al ingresar "calculo": + * El usuario es guiado para introducir números y operadores secuencialmente para realizar operaciones básicas. + * El usuario puede utilizar "resultado" en la secuencia de cálculo para reutilizar el resultado almacenado en la calculadora. + * El cálculo finaliza al pulsar , volviendo y actualizando el resultado almacenado de la calculadora con el cálculo realizado. + * También podemos escribir "cancelar", volviendo sin realizar ningún cambio en el resultado almacenado de la calculadora. + + 4. La calculadora sigue ejecutándose hasta que el usuario confirma la salida al ingresar una entrada vacía y pulsar . + + 5. Finalmente, se limpia la pantalla, el programa se despide con el mensaje "\n\nBye, bye...\n\n" y termina. + """ + # TODO: Corrige los errores y haz que el main funcione correctamente... + + decimales = 2 + resultado = 0.0 + desea_salir = False + + while not desea_salir: + + print (f"El resultado almacenado por defecto es: {resultado}") + + pausa (tecla_enter = True) + + limpiar_pantalla() + + print(obtener_operaciones()) + + + entrada = pedir_entrada(f"Operación (RES => {resultado}) >> ").lower().strip() + + if entrada == "": + entrada = pedir_entrada("¿Desea salir de la calculadora? (s/n) ") + if entrada == "s": + print("Bye, bye...") + desea_salir = True + + + + + + elif entrada == "ce": + resultado = 0 + + + elif entrada.startswith("decimales"): + # Extraemos las posiciones decimales y las convertimos a un valor entero + decimales = str(entrada.split()[1]) + print(f"Decimales configurados a {decimales}.") + + elif entrada == "calculo": + realizar_calculo(decimales, resultado,num1, num2) + + else: + mostrar_error() + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/dam1_calculadora/emma_calculadora_alumnos.py b/dam1_calculadora/emma_calculadora_alumnos.py new file mode 100644 index 0000000..0f72111 --- /dev/null +++ b/dam1_calculadora/emma_calculadora_alumnos.py @@ -0,0 +1,459 @@ +# TODO: ATENCIÓN!!! +# 1. Importar los paquetes necesarios +# 2. Usar la función mostrar_error para imprimir los errores por consola. +# 3. Los comentarios TODO os ayudan a resolver cada apartado de esta prueba. +# 4. CADA función SOLO puede tener una instrucción return. +# 5. En test_calculadora_alumnos.py crear el test unitario para la función es_resultado_negativo y hacer que todos los test unitarios se cumplan. +import os + + +# Mensajes de error predefinidos +MENSAJES_ERROR = ( + f"Problemas al intentar limpiar la pantalla {error}", + f"Error al configurar los decimales. Formato: decimales .", + f"Entrada no válida. Ingrese número, operador, 'resultado', 'cancelar' o para finalizar el cálculo.", + f"Error: Introduzca un operador antes de otro número.", + f"Comando no reconocido. Escriba 'lista' para ver las operaciones disponibles.", + f"Error: no es posible la división por 0! Introduzca otro valor diferente a 0...", + f"Se produjo un error: {error}" +) + +# Operadores soportados por la calculadora +OPERADORES = '+', '-', 'x','/','**' + +# TODO: Ayuda: incluye en esta constante los símbolos que reconocerá la aplicación para el cálculo de las operaciones: +# '+' para la suma. +# '-' para la resta. +# 'x' o '*' para la multiplicación. +# '/' o ':' para la división. +# '**' o 'exp' para la potencia. + + + +def limpiar_pantalla(): + """ + Limpia la consola según el sistema operativo. + """ + # TODO: El desarrollo de esta función está incompleto y con errores. + try: + os.system ("clear" if os.name == "posix" else "cls") + # Otra forma de expresar la misma instrucción sería la siguiente: + # if os.name = posix: + # os.system(clear) + # else: + # os.system(cls) + + except Exception as e: + mostrar_error(str(e)) + +def pausa(): + """ + Pausa la ejecución del programa hasta que se pulse ENTER... "\nPresione ENTER para continuar..." + """ + # TODO: Desarrollar esta función según su documentación. + + input("\nPresione ENTER [ ↩ ] para continuar....") + +def mostrar_error(indice_error: int, msj_error = None): + """ + Muestra un mensaje de error en la consola. + + Args: + indice_error (int): Índice del mensaje de error en MENSAJES_ERROR. + msj_error (str, opcional): Texto adicional para personalizar el mensaje de error. + """ + # TODO: + # 1. Corrige el error que existe a la hora de acceder a los mensajes de error que están en la constante + # MENSAJES_ERROR. A los elementos de una tupla se accede igual que a los caracteres de una + # cadena de caracteres. + # 2. Completa el código de esta función para que controle específicamente las excepciones IndexError y + # muestre el mensaje: "\n*ERROR* Mensaje de error no definido.\n" + # 3. También se pide que se controle cualquier otra excepción que se pueda producir y muestre el mensaje: + # "\n*ERROR* Problemas al mostrar error!\n{e}\n" + # 4. En esta función los mensajes de error deben mostrarse con print. + + mensaje = MENSAJES_ERROR[indice_error] + try: + if msj_error: + mensaje = mensaje.format(error = msj_error) + print(f"**ERR0R** {mensaje}") + + if msj_error != None: + raise IndexError(f"\n*ERROR* {MENSAJES_ERROR.format(error = msj_error)}\n") + else: + raise ValueError (f"\n*ERROR* {MENSAJES_ERROR}\n") + + except IndexError: + print(f"\n*ERROR* Mensaje de error no definido.\n") + except ValueError: + print(f"\n*ERROR* Problemas al mostrar error!\n{e}\n") + + except Exception as e: + print(e) + + # if msj_error != None: + # raise IndexError(f"\n*ERROR* {MENSAJES_ERROR.format(error = msj_error)}\n") + # else: + # raise ValueError (f"\n*ERROR* {MENSAJES_ERROR}\n") + +def sumar(num1: float, num2: float) -> float: + """Funcion para una simple suma:""" + # TODO: Realizar el desarrollo completo, incluida la documentación... recibe 2 números float y retorna la suma de ambos. + + return num1 + num2 + +def restar(num1: float, num2: float) -> float: + """Una simple resta:""" + # TODO: Realizar el desarrollo completo, incluida la documentación... recibe 2 números float y retorna la resta de ambos. + + return num1 - num2 + +def es_resultado_negativo(num1: float, num2: float) -> bool: + """Determina si el resultado de una operación entre num1 y num2 debe ser negativo.""" + # TODO: Realizar el desarrollo completo de esta función teniendo en cuenta la documentación y completándola también. + # Debe pasar las pruebas unitarias. + # Se trata de una función que os puede venir bien para utilizarla tanto en la función multiplicar, como en dividir. + # Ya que va a determinar si la multiplicación o división entre dos números debería ser de signo negativo o no. + + return(num1 < 0 and num2 > 0) or (num1 > 0 and num2 < 0) + +def multiplicar(num1: float, num2: float) -> float: + """ + Realiza la multiplicación ENTERA de dos números usando solo sumas y restas. + + Args: + num1 (float): Primer número. + num2 (float): Segundo número. + + Returns: + int: Resultado de la multiplicación. + + Note: + Debe redondear los números recibidos a enteros para trabajar. + """ + # TODO: + # 1. Realizar el desarrollo completo de esta función teniendo en cuenta la documentación. + # 2. Esta función debe pasar las pruebas unitarias. + # 3. No podéis usar el operador de multiplicación de Python para realizar el desarrollo de la misma, + # es decir, que debéis realizar la multiplicación con SUMAS y/o RESTAS... + # 4. Aunque se reciben números de tipo float, debéis redondearlos como números enteros para + # simplificar esta función, es decir, la operación 4.98 * 3.33, deberá convertirse en 5 * 3. + # 5. Tened en cuenta que podéis recibir números negativos, es decir, la operación -5 * -5 = 25 + # 6. OBLIGATORIO usar un bucle for. + # 7. Incluir algún comentario para mejorar la claridad y permitir que otros comprendan el propósito y funcionamiento del código. + + + + num1 = round(num1) + num2 = round(num2) + + resultado = 0 + negativo = es_resultado_negativo(num1, num2) + + + + for _ in range(num2): + resultado += num1 + + if num1 and num2 == negativo: + + return round(-resultado) + else: + return round(resultado) + +def dividir(num1: float, num2: float) -> float: + """ + Realiza la división ENTERA de dos números usando solo sumas y restas. + + Args: + num1 (float): Dividendo. + num2 (float): Divisor. + + Returns: + int: Resultado de la división. + + Raises: + ZeroDivisionError: Si el divisor es cero. + + Note: + Debe redondear los números recibidos a enteros para trabajar. + """ + # TODO: + # 1. Realizar el desarrollo completo de esta función teniendo en cuenta la documentación. + # 2. Esta función debe pasar las pruebas unitarias. + # 3. No podéis usar el operador de división de Python para realizar el desarrollo de la misma, + # es decir, que debéis realizar la división con SUMAS y/o RESTAS... + # 4. Aunque se reciben números de tipo float, debéis redondearlos como números enteros para + # simplificar esta función, es decir, la operación 4.98 / 3.33, deberá convertirse en 5 / 3. + # 5. Tened en cuenta que podéis recibir números negativos, es decir, la operación -5 / -5 = 1 + # 6. Incluir algún comentario para mejorar la claridad y permitir que otros comprendan el propósito y funcionamiento del código. + if num2 == 0: + raise ZeroDivisionError("No se puede dividir entre cero.") + + num1 = round(num1) + num2 = round(num2) + + resultado = 0 + negativo = es_resultado_negativo(num1, num2) + num1, num2 = (num1), (num2) + + while num1 >= num2: + num1 = num1 - num2 + resultado += 1 + if num1 and num2 == negativo: + + return -resultado + else: + return resultado + +def potencia(num1: float, num2: float) -> float: + # TODO: + # 1. Realizar el desarrollo completo de esta función y su documentación. + # 2. Esta función debe pasar las pruebas unitarias. + # 3. PREMISAS a tener en cuenta: + # - Cualquier número elevado a 0 da como resultado 1. + # - Para simplificar esta función, vamos a suponer que un número elevado a un + # exponente negativo siempre dará 0 (aunque en realidad no es así matemáticamente) + # 4. Utiliza la función de multiplicar para realizar los cálculos que te + # harán falta en esta función (RECUERDA que no puedes usar directamente los operadores + # de Python para la multiplicación y división). + # 5. Incluir algún comentario para mejorar la claridad y permitir que otros comprendan el propósito y funcionamiento del código. + base = round(base) + exponente = round(exponente) + + resultado = 0 + + if exponente == 0: + print(1) + if exponente < 0: + print(0) + + resultado = 1 + for _ in range(exponente): + resultado = multiplicar(resultado, base) + + return resultado + +def pedir_entrada(msj: str) -> str: + """ + Pide al usuario una entrada, elimina espacios por delante y por detrás y la convierte a minúsculas. + + Args: + msj (str): Mensaje para solicitar la entrada. + + Returns: + str: Entrada del usuario. + """ + # TODO: El desarrollo de esta función está incompleto, debéis terminarla teniendo en cuenta la documentación + + return input(msj).strip().lower() + +def calcular_operacion(num1: float, num2: float, operador: str) -> float: + """ + Realiza la operación especificada entre num1 y num2 dependiendo del valor del operador. + + Args: + num1 (float): Primer número. + num2 (float): Segundo número. + operador (str): Operador de la operación. + + Returns: + float: Resultado de la operación. + + Raises: + ZeroDivisionError: Si el divisor es cero. + """ + # TODO: El desarrollo de esta función está incompleto... completadla teniendo en cuenta la documentación + # y que debe realizar las llamadas adecuadas a las funciones ya creadas para realizar los distintos + # cálculos... sumar, restar, multiplicar, dividir y potencia. + # IMPORTANTE: Si hacemos caso a la documentación de esta función, NO debéis capturar la excepción + # ZeroDivisionError aquí, sino que hay que dejadla que se propague a la función llamante, realizar_calculo(), + # dónde se os indica cómo realizar la gestión de estos errores. + if operador == '+': + return sumar(num1, num2) + elif operador == '-': + return restar(num1, num2) + elif operador in ('x', '*'): + return multiplicar(num1, num2) + elif operador in ('/', ':'): + return dividir(num1, num2) + elif operador in ('**', 'exp'): + return potencia(num1, num2) + else: + mostrar_error(4) + return None + + + return resultado + +def obtener_operaciones() -> str: + """ + Devuelve una cadena con la lista de operaciones disponibles en la calculadora. + + Returns: + (str): cadena de caracteres con la información de las operaciones disponibles. + """ + # TODO: El desarrollo de esta función está incompleto... ver documentación para solucionarla correctamente. + return """ + Operaciones disponibles: + ce => Reiniciar resultado a 0 + decimales => Establecer decimales en resultado + cadena vacía + => Pregunta si desea salir + calculo => Iniciar cálculo secuencial + + => Suma + - => Resta + x o * => Multiplicación + / o : => División + ** o exp => Potencia + cancelar => vovler sin actualizar resultado de la calculadora + cadena vacía + => volver actualizando resultado de la calculadora + """ + +def realizar_calculo(resultado_almacenado: int, decimales: float): + """ + Realiza una secuencia de cálculos solicitando números y operadores al usuario. + + Args: + decimales (int): Número de decimales para el resultado. + resultado_almacenado (float): Valor almacenado en la calculadora. + + Returns: + float: Resultado final del cálculo o None si se cancela. + + Note: + * Dentro de esta función el usuario puede realizar cálculos secuenciales, es decir, + comenzará introduciendo un número, después un operador, y otro número... a partir + de aquí sobre el resultado acumulado, introducirá operador y número para seguir + realizando cálculos (ver ejemplos en README.md de la tarea en el repositorio de GitHub). + * El usuario es guiado para introducir números y operadores secuencialmente + para realizar operaciones básicas. + * El usuario puede utilizar "resultado" en la secuencia de cálculo para reutilizar el + resultado almacenado en la calculadora. + * El cálculo finaliza al pulsar , volviendo y actualizando el resultado almacenado + de la calculadora con el cálculo realizado. + * También podemos escribir "cancelar", volviendo sin realizar ningún cambio en el + resultado almacenado de la calculadora. + """ + # TODO: El desarrollo de esta función está incompleto... ver documentación para solucionarla correctamente. + + operador = None + resultado = None + realizando_calculos = True + + print("\n## Ingrese número, operador, 'resultado', 'cancelar' o para finalizar el cálculo ##\n") + + while realizando_calculos: + entrada = pedir_entrada(f"\t (Cálculo = resultado) >> ") + + if entrada == "cancelar": + return None + + + elif entrada == "": + return resultado + + + elif entrada in OPERADORES: + operador = entrada + + else: + # TODO: este código funciona cuando solucionéis que reconozca las variables resultado_almacenado y decimales. + # Pero no gestiona los posibles tipos de Excepciones que se pueden producir: + # - ValueError que debe mostrar el error que está en la posición 2 de MENSAJES_ERROR. + # - ZeroDivisionError que debe mostrar el error que está en la posición 5 de MENSAJES_ERROR. + # - Exception que debe mostrar el error que está en la posición 6 de MENSAJES_ERROR. + if entrada == "resultado": + entrada = resultado_almacenado + + try: + numero = float(entrada) + + if operador is not None: + if resultado is None: + resultado = 0 + resultado = round(calcular_operacion(resultado, numero, operador), decimales) + operador = None + + elif resultado is None: + resultado = numero + + else: + mostrar_error() + except ValueError: + mostrar_error() + except ZeroDivisionError: + mostrar_error() + except Exception as e: + mostrar_error(6, str(e)) + +def desea_salir() -> bool: + + return input("¿Desea salir de la calculadora? (s/n) ").strip().lower() != 'n' + +def main(): + """ + Función principal de la calculadora. Gestiona la entrada del usuario y coordina las operaciones. + + Note: + El flujo del programa es el siguiente: + + 1. Inicia la calculadora mostrando el resultado almacenado por defecto (0.00). + + 2. El usuario ingresa un comando, que puede ser: + - "lista" para ver todas las operaciones disponibles. + - "ce" para reiniciar el resultado almacenado a 0. + - "decimales " para establecer el número de decimales mostrados en el resultado. + - "calculo" para iniciar una secuencia de cálculo paso a paso. + - Una entrada vacía y pulsa la tecla para salir de la calculadora. + + 3. Según el comando ingresado: + - El programa realiza la operación o ejecuta la acción indicada. + - Al ingresar "calculo": + * El usuario es guiado para introducir números y operadores secuencialmente para realizar operaciones básicas. + * El usuario puede utilizar "resultado" en la secuencia de cálculo para reutilizar el resultado almacenado en la calculadora. + * El cálculo finaliza al pulsar , volviendo y actualizando el resultado almacenado de la calculadora con el cálculo realizado. + * También podemos escribir "cancelar", volviendo sin realizar ningún cambio en el resultado almacenado de la calculadora. + + 4. La calculadora sigue ejecutándose hasta que el usuario confirma la salida al ingresar una entrada vacía y pulsar . + + 5. Finalmente, se limpia la pantalla, el programa se despide con el mensaje "\n\nBye, bye...\n\n" y termina. + """ + # TODO: Corrige los errores y haz que el main funcione correctamente... + + decimales = 2 + resultado = 0.0 + + while not desea_salir(): + entrada = pedir_entrada(f"Operación (RES => {resultado}) >> ") + + if entrada == "": + if desea_salir(): + False + + elif entrada == "lista": + print(obtener_operaciones()) + + elif entrada == "ce": + resultado = 0.0 + print(f"Resultado reiniciado a {resultado}.") + + elif entrada.startswith("decimales"): + try: + decimales = int(entrada.split()[1]) + print(f"Decimales configurados a {decimales}.") + except (IndexError, ValueError): + mostrar_error(1) + + elif entrada == "calculo": + resultado_calculo = realizar_calculo(decimales, resultado) + if resultado_calculo is not None: + resultado = resultado_calculo + + else: + mostrar_error(4) + + limpiar_pantalla() + print("\n\nBye, bye...\n\n") + +if __name__ == "__main__": + main() diff --git a/dam1_calculadora/fran_calculadora_alumnos.py b/dam1_calculadora/fran_calculadora_alumnos.py new file mode 100644 index 0000000..563b1d8 --- /dev/null +++ b/dam1_calculadora/fran_calculadora_alumnos.py @@ -0,0 +1,586 @@ +# TODO: ATENCIÓN!!! +# 1. Importar los paquetes necesarios +# 2. Usar la función mostrar_error para imprimir los errores por consola. +# 3. Los comentarios TODO os ayudan a resolver cada apartado de esta prueba. +# 4. CADA función SOLO puede tener una instrucción return. +# 5. En test_calculadora_alumnos.py crear el test unitario para la función es_resultado_negativo y hacer que todos los test unitarios se cumplan. + + +import os +import time + + +# Mensajes de error predefinidos +MENSAJES_ERROR = ( + "Problemas al intentar limpiar la pantalla {error}", + "Error al configurar los decimales. Formato: decimales .", + "Entrada no válida. Ingrese número, operador, 'resultado', 'cancelar' o para finalizar el cálculo.", + "Error: Introduzca un operador antes de otro número.", + "Comando no reconocido. Escriba 'lista' para ver las operaciones disponibles.", + "Error: no es posible la división por 0! Introduzca otro valor diferente a 0...", + "Se produjo un error: {error}" +) + +# Operadores soportados por la calculadora +OPERADORES = ["+", "-", "x", "*", "/", ":", "**", "exp"] + + + +# TODO: Ayuda: incluye en esta constante los símbolos que reconocerá la aplicación para el cálculo de las operaciones: +# '+' para la suma. +# '-' para la resta. +# 'x' o '*' para la multiplicación. +# '/' o ':' para la división. +# '**' o 'exp' para la potencia. + + + +def limpiar_pantalla(): + """ + Limpia la consola según el sistema operativo. + """ + # TODO: El desarrollo de esta función está incompleto y con errores. + try: + # Otra forma de expresar la misma instrucción sería la siguiente: + if os.name == 'nt': + os.system('cls') + else: + os.system('clear') + + except Exception as e: + print(f"**ERROR**, {e}") + + + +def pausa(): + """ + Pausa la ejecución del programa hasta que se pulse ENTER... "\nPresione ENTER para continuar..." + """ + # TODO: Desarrollar esta función según su documentación. + + + + tiempo = None + while True: + time.sleep(tiempo) + + if not tiempo: + break + + + + +def mostrar_error(indice_error: int, msj_error = None): + """ + Muestra un mensaje de error en la consola. + + Args: + indice_error (int): Índice del mensaje de error en MENSAJES_ERROR. + msj_error (str, opcional): Texto adicional para personalizar el mensaje de error. + """ + # TODO: + # 1. Corrige el error que existe a la hora de acceder a los mensajes de error que están en la constante + # MENSAJES_ERROR. A los elementos de una tupla se accede igual que a los caracteres de una + # cadena de caracteres. + # 2. Completa el código de esta función para que controle específicamente las excepciones IndexError y + # muestre el mensaje: "\n*ERROR* Mensaje de error no definido.\n" + # 3. También se pide que se controle cualquier otra excepción que se pueda producir y muestre el mensaje: + # "\n*ERROR* Problemas al mostrar error!\n{e}\n" + # 4. En esta función los mensajes de error deben mostrarse con print. + if msj_error != None: + print(f"\n*ERROR* {MENSAJES_ERROR.format(error = msj_error)}\n") + else: + print(f"\n*ERROR* {MENSAJES_ERROR}\n") + + +def sumar(num1: float, num2: float) -> float: + # TODO: Realizar el desarrollo completo, incluida la documentación... recibe 2 números float y retorna la suma de ambos. + + suma = num1 + num2 + + return suma + + + +def restar(num1: float, num2: float) -> float: + # TODO: Realizar el desarrollo completo, incluida la documentación... recibe 2 números float y retorna la resta de ambos. + + resta = num1 - num2 + + return resta + + +def es_resultado_negativo(num1: float, num2: float) -> bool: + """Determina si el resultado de una operación entre num1 y num2 debe ser negativo.""" + # TODO: Realizar el desarrollo completo de esta función teniendo en cuenta la documentación y completándola también. + # Debe pasar las pruebas unitarias. + # Se trata de una función que os puede venir bien para utilizarla tanto en la función multiplicar, como en dividir. + # Ya que va a determinar si la multiplicación o división entre dos números debería ser de signo negativo o no. + + + + if num1 < 0 and num2 > 0: + return True + + elif num1 > 0 and num2 < 0: + return True + + elif num1 == 0 or num2 == 0: + return False + + elif num1 > 0 and num2 > 0: + return False + + elif num1 < 0 and num2 < 0: + return False + + + +def multiplicar(num1: float, num2:float) -> int: + """ + Realiza la multiplicación ENTERA de dos números usando solo sumas y restas. + + Args: + num1 (float): Primer número. + num2 (float): Segundo número. + + Returns: + int: Resultado de la multiplicación. + + Note: + Debe redondear los números recibidos a enteros para trabajar. + """ + # TODO: + # 1. Realizar el desarrollo completo de esta función teniendo en cuenta la documentación. + # 2. Esta función debe pasar las pruebas unitarias. + # 3. No podéis usar el operador de multiplicación de Python para realizar el desarrollo de la misma, + # es decir, que debéis realizar la multiplicación con SUMAS y/o RESTAS... + # 4. Aunque se reciben números de tipo float, debéis redondearlos como números enteros para + # simplificar esta función, es decir, la operación 4.98 * 3.33, deberá convertirse en 5 * 3. + # 5. Tened en cuenta que podéis recibir números negativos, es decir, la operación -5 * -5 = 25 + # 6. OBLIGATORIO usar un bucle for. + # 7. Incluir algún comentario para mejorar la claridad y permitir que otros comprendan el propósito y funcionamiento del código. + + + num1 = round(num1) + num2 = round(num2) + + + resultado = 0 + negativo = False + + + #Si uno de los dos números es negativo y el otro positivo, tendremos asegurado que el resultado será negativo + if (num1 < 0 and num2 > 0) or (num1 > 0 and num2 < 0): + negativo = True + + + + #Para que no haya decimales en la operación, de esta forma la simplificaremos + num1 = abs(num1) + num2 = abs(num2) + + + #OBLIGATORIO usar un bucle for para realizar la operación + for _ in range (num1): + resultado += num2 + + + #En caso de que sea negativo, le pondremos al resultado el signo menos ('-') + + if negativo: + resultado = -resultado + + + + return resultado + + + + + + + +def dividir(num1: float, num2: float) -> int: + """ + Realiza la división ENTERA de dos números usando solo sumas y restas. + + Args: + num1 (float): Dividendo. + num2 (float): Divisor. + + Returns: + int: Resultado de la división. + + Raises: + ZeroDivisionError: Si el divisor es cero. + + Note: + Debe redondear los números recibidos a enteros para trabajar. + """ + # TODO: + # 1. Realizar el desarrollo completo de esta función teniendo en cuenta la documentación. + # 2. Esta función debe pasar las pruebas unitarias. + # 3. No podéis usar el operador de división de Python para realizar el desarrollo de la misma, + # es decir, que debéis realizar la división con SUMAS y/o RESTAS... + # 4. Aunque se reciben números de tipo float, debéis redondearlos como números enteros para + # simplificar esta función, es decir, la operación 4.98 / 3.33, deberá convertirse en 5 / 3. + # 5. Tened en cuenta que podéis recibir números negativos, es decir, la operación -5 / -5 = 1 + # 6. Incluir algún comentario para mejorar la claridad y permitir que otros comprendan el propósito y funcionamiento del código. + + + num1 = round(num1) + num2 = round(num2) + + #No estará permitido dividir entre 0 y saltará **ERROR** + if num2 == 0: + raise ZeroDivisionError ("El divisor no puede ser 0") + + negativo = (num1 < 0) ^ (num2 < 0) + + + #Para que no haya decimales en la operación, de esta forma la simplificaremos + num1 = abs(num1) + num2 = abs(num2) + + + + cociente = 0 + acumulador = num1 + + + while acumulador >= num2: + acumulador -= num2 + cociente += 1 + + + + #En caso de que sea negativo, le pondremos el signo menos ('-') + if negativo: + cociente = -cociente + + + return cociente + + + +def potencia(num1: float, num2: float) -> int: + # TODO: + # 1. Realizar el desarrollo completo de esta función y su documentación. + # 2. Esta función debe pasar las pruebas unitarias. + # 3. PREMISAS a tener en cuenta: + # - Cualquier número elevado a 0 da como resultado 1. + # - Para simplificar esta función, vamos a suponer que un número elevado a un + # exponente negativo siempre dará 0 (aunque en realidad no es así matemáticamente) + # 4. Utiliza la función de multiplicar para realizar los cálculos que te + # harán falta en esta función (RECUERDA que no puedes usar directamente los operadores + # de Python para la multiplicación y división). + # 5. Incluir algún comentario para mejorar la claridad y permitir que otros comprendan el propósito y funcionamiento del código. + + + num1 = round(num1) + num2 = round(num2) + + + resultado = 0 + negativo = False + + + #Si uno de los dos números es negativo y el otro positivo, tendremos asegurado que el resultado será negativo. + if (num1 < 0 and num2 > 0) or (num1 > 0 and num2 < 0): + negativo = True + + + #Para que no haya decimales en la operación, de esta forma la simplificaremos. + num1 = abs(num1) + num2 = abs(num2) + + + #OBLIGATORIO usar un bucle for para realizar la operación. + for _ in range (num1): + resultado += num2 + + + base = round(base) + exponente = round(exponente) + + #Cualquier número elevado a 0 da como resultado 1. + if exponente == 0: + return 1 + + #Exponente negativo siempre dará 0 (aunque en realidad no es así matemáticamente). + if exponente < 0: + return 0 + + + resultado = 1 + + for _ in range (exponente): + resultado = multiplicar (resultado, base) + + + return resultado + + + + +def pedir_entrada(msj: str) -> str: + + msj = input("¿Desea salir?: ").strip().lower() + return input(msj) + + + + # TODO: El desarrollo de esta función está incompleto, debéis terminarla teniendo en cuenta la documentación + + + + +def calcular_operacion(num1: float, num2: float, operador: str) -> float: + """ + Realiza la operación especificada entre num1 y num2 dependiendo del valor del operador. + + Args: + num1 (float): Primer número. + num2 (float): Segundo número. + operador (str): Operador de la operación. + + Returns: + float: Resultado de la operación. + + Raises: + ZeroDivisionError: Si el divisor es cero. + """ + # TODO: El desarrollo de esta función está incompleto... completadla teniendo en cuenta la documentación + # y que debe realizar las llamadas adecuadas a las funciones ya creadas para realizar los distintos + # cálculos... sumar, restar, multiplicar, dividir y potencia. + # IMPORTANTE: Si hacemos caso a la documentación de esta función, NO debéis capturar la excepción + # ZeroDivisionError aquí, sino que hay que dejadla que se propague a la función llamante, realizar_calculo(), + # dónde se os indica cómo realizar la gestión de estos errores. + + + resultado = "" + + if operador == "+": + sumar() + + + elif operador == "-": + restar() + + elif operador == "x" or "*": + multiplicar() + + elif operador == "/" or ":": + dividir() + + elif operador == "exp" or "**": + potencia() + + + else: + print("Por favor, elige uno de los operadores disponibles") + + + + return resultado + + + +def obtener_operaciones() -> str: + """ + Devuelve una cadena con la lista de operaciones disponibles en la calculadora. + + Returns: + (str): cadena de caracteres con la información de las operaciones disponibles. + """ + # TODO: El desarrollo de esta función está incompleto... ver documentación para solucionarla correctamente. + """ + + + + Operaciones disponibles: + ce => Reiniciar resultado a 0 + decimales => Establecer decimales en resultado + cadena vacía + => Pregunta si desea salir + calculo => Iniciar cálculo secuencial + + => Suma + - => Resta + x o * => Multiplicación + / o : => División + ** o exp => Potencia + cancelar => vovler sin actualizar resultado de la calculadora + cadena vacía + => volver actualizando resultado de la calculadora + """ + + print("\nOperaciones disponibles:") + print("ce => Reiniciar resultado a 0") + print("decimales => Establecer decimales en resultado") + print("cadena vacía + => Pregunta si desea salir") + print("calculo => Iniciar cálculo secuencial") + print("+ => Suma") + print("- => Resta") + print("x o * => Multiplicación") + print("/ o : => División") + print("** o exp => Potencia") + print("cancelar => vovler sin actualizar resultado de la calculadora") + print("cadena vacía + => volver actualizando resultado de la calculadora") + + return None + + + +def realizar_calculo(): + """ + Realiza una secuencia de cálculos solicitando números y operadores al usuario. + + Args: + decimales (int): Número de decimales para el resultado. + resultado_almacenado (float): Valor almacenado en la calculadora. + + Returns: + float: Resultado final del cálculo o None si se cancela. + + Note: + * Dentro de esta función el usuario puede realizar cálculos secuenciales, es decir, + comenzará introduciendo un número, después un operador, y otro número... a partir + de aquí sobre el resultado acumulado, introducirá operador y número para seguir + realizando cálculos (ver ejemplos en README.md de la tarea en el repositorio de GitHub). + * El usuario es guiado para introducir números y operadores secuencialmente + para realizar operaciones básicas. + * El usuario puede utilizar "resultado" en la secuencia de cálculo para reutilizar el + resultado almacenado en la calculadora. + * El cálculo finaliza al pulsar , volviendo y actualizando el resultado almacenado + de la calculadora con el cálculo realizado. + * También podemos escribir "cancelar", volviendo sin realizar ningún cambio en el + resultado almacenado de la calculadora. + """ + # TODO: El desarrollo de esta función está incompleto... ver documentación para solucionarla correctamente. + + operador = None + resultado = None + realizando_calculos = True + + print("\n## Ingrese número, operador, 'resultado', 'cancelar' o para finalizar el cálculo ##\n") + + while realizando_calculos: + entrada = pedir_entrada(f"\t (Cálculo = resultado) >> ") + + if entrada == "cancelar": + return None + + + elif entrada == "": + entrada = resultado + return resultado + + + elif entrada in OPERADORES: + operador = ["+", "-", "x", "*", "/", ":", "**", "exp"] + return operador + + else: + # TODO: este código funciona cuando solucionéis que reconozca las variables resultado_almacenado y decimales. + # Pero no gestiona los posibles tipos de Excepciones que se pueden producir: + # - ValueError que debe mostrar el error que está en la posición 2 de MENSAJES_ERROR. + # - ZeroDivisionError que debe mostrar el error que está en la posición 5 de MENSAJES_ERROR. + # - Exception que debe mostrar el error que está en la posición 6 de MENSAJES_ERROR. + if entrada == "resultado": + entrada = resultado_almacenado + + try: + numero = float(entrada) + + if operador is not None: + if resultado is None: + resultado = 0 + resultado = round(calcular_operacion(resultado, numero, operador), decimales) + operador = None + + elif resultado is None: + resultado = numero + + else: + mostrar_error(3) + except ValueError: + mostrar_error() + + +def main(): + """ + Función principal de la calculadora. Gestiona la entrada del usuario y coordina las operaciones. + + Note: + El flujo del programa es el siguiente: + + 1. Inicia la calculadora mostrando el resultado almacenado por defecto (0.00). + + 2. El usuario ingresa un comando, que puede ser: + - "lista" para ver todas las operaciones disponibles. + - "ce" para reiniciar el resultado almacenado a 0. + - "decimales " para establecer el número de decimales mostrados en el resultado. + - "calculo" para iniciar una secuencia de cálculo paso a paso. + - Una entrada vacía y pulsa la tecla para salir de la calculadora. + + 3. Según el comando ingresado: + - El programa realiza la operación o ejecuta la acción indicada. + - Al ingresar "calculo": + * El usuario es guiado para introducir números y operadores secuencialmente para realizar operaciones básicas. + * El usuario puede utilizar "resultado" en la secuencia de cálculo para reutilizar el resultado almacenado en la calculadora. + * El cálculo finaliza al pulsar , volviendo y actualizando el resultado almacenado de la calculadora con el cálculo realizado. + * También podemos escribir "cancelar", volviendo sin realizar ningún cambio en el resultado almacenado de la calculadora. + + 4. La calculadora sigue ejecutándose hasta que el usuario confirma la salida al ingresar una entrada vacía y pulsar . + + 5. Finalmente, se limpia la pantalla, el programa se despide con el mensaje "\n\nBye, bye...\n\n" y termina. + """ + # TODO: Corrige los errores y haz que el main funcione correctamente... + + decimales = 2 + resultado = 0.0 + resultado_almacenado = 0 + + + desea_salir = pedir_entrada() + entrada = ["lista", "ce", "decimales", "calculo", "" ] + + + while desea_salir: + break + + + while not desea_salir: + + limpiar_pantalla() + + print("\n### CALCULADORA ###") + print("-----------") + pedir_entrada = print(input(f"Operación (RES => {resultado} >> ")) + + + if entrada == "": + pedir_entrada("¿Desea salir de la calculadora? (s/n) ") + + elif entrada == "lista": + obtener_operaciones + + elif entrada == "ce": + resultado_almacenado = 0 + + + elif entrada.startswith("decimales"): + # Extraemos las posiciones decimales y las convertimos a un valor entero + decimales = str(entrada.split()[1]) + print(f"Decimales configurados a {decimales}.") + + elif entrada == "calculo": + realizar_calculo(decimales, resultado) + + else: + mostrar_error() + + + + +if __name__ == "__main__": + main() diff --git a/dam1_calculadora/gonzalo_calculadora_alumnos.py b/dam1_calculadora/gonzalo_calculadora_alumnos.py new file mode 100644 index 0000000..c81add7 --- /dev/null +++ b/dam1_calculadora/gonzalo_calculadora_alumnos.py @@ -0,0 +1,550 @@ +# TODO: ATENCIÓN!!! +# 1. Importar los paquetes necesarios +# 2. Usar la función mostrar_error para imprimir los errores por consola. +# 3. Los comentarios TODO os ayudan a resolver cada apartado de esta prueba. +# 4. CADA función SOLO puede tener una instrucción return. +# 5. En test_calculadora_alumnos.py crear el test unitario para la función es_resultado_negativo y hacer que todos los test unitarios se cumplan. + +import os +import time +import keyboard + +# Mensajes de error predefinidos +MENSAJES_ERROR = ( + "Problemas al intentar limpiar la pantalla {error}", + "Error al configurar los decimales. Formato: decimales .", + "Entrada no válida. Ingrese número, operador, 'resultado', 'cancelar' o para finalizar el cálculo.", + "Error: Introduzca un operador antes de otro número.", + "Comando no reconocido. Escriba 'lista' para ver las operaciones disponibles.", + "Error: no es posible la división por 0! Introduzca otro valor diferente a 0...", + "Se produjo un error: {error}" +) + +# Operadores soportados por la calculadora +OPERADORES = ['+', '-', 'x', '*', '/', ':', '**', 'exp'] +# TODO: Ayuda: incluye en esta constante los símbolos que reconocerá la aplicación para el cálculo de las operaciones: +# '+' para la suma. +# '-' para la resta. +# 'x' o '*' para la multiplicación. +# '/' o ':' para la división. +# '**' o 'exp' para la potencia. + + + +def limpiar_pantalla(): + """ + Limpia la consola según el sistema operativo. + """ + # TODO: El desarrollo de esta función está incompleto y con errores. + # Otra forma de expresar la misma instrucción sería la siguiente: + # if os.name = posix: + # os.system(clear) + # else: + # os.system(cls) + try: + if os.name == 'posix': + os.system('clear') + else: + os.system('cls') + + except Exception as e: + mostrar_error(0, e) + + + +def pausa(): + """ + Pausa la ejecución del programa hasta que se pulse ENTER... "\nPresione ENTER para continuar..." + """ + # TODO: Desarrollar esta función según su documentación. + + print('\nPresione ENTER para continuar...') + keyboard.wait('enter') + + +def mostrar_error(indice_error: int, msj_error = None): + """ + Muestra un mensaje de error en la consola. + + Args: + indice_error (int): Índice del mensaje de error en MENSAJES_ERROR. + msj_error (str, opcional): Texto adicional para personalizar el mensaje de error. + """ + # TODO: + # 1. Corrige el error que existe a la hora de acceder a los mensajes de error que están en la constante + # MENSAJES_ERROR. A los elementos de una tupla se accede igual que a los caracteres de una + # cadena de caracteres. + # 2. Completa el código de esta función para que controle específicamente las excepciones IndexError y + # muestre el mensaje: "\n*ERROR* Mensaje de error no definido.\n" + # 3. También se pide que se controle cualquier otra excepción que se pueda producir y muestre el mensaje: + # "\n*ERROR* Problemas al mostrar error!\n{e}\n" + # 4. En esta función los mensajes de error deben mostrarse con print. + try: + if msj_error != None: + print(f"\n*ERROR* {MENSAJES_ERROR[indice_error].format(error = msj_error)}\n") + else: + print(f"\n*ERROR* {MENSAJES_ERROR[indice_error]}\n") + except IndexError: + print('\n*ERROR* Mensaje de error no definido.\n') + except Exception as e: + print(f'\n*ERROR* Problemas al mostrar error!\n{e}\n') + + + +def sumar(num1: float, num2: float): + """ + Realiza una suma simple entre dos numeros + + Args: + num (float): Primer numero para la operación + num1 (float): Segundo número para la operación. + Returns: + int: Resultado de la operación + """ + # TODO: Realizar el desarrollo completo, incluida la documentación... recibe 2 números float y retorna la suma de ambos. + return num1 + num2 + + +def restar(num1: float, num2: float): + """ + Resuelve una resta simple entre dos numeros + + Args: + num (float): Primer numero para la operación + num1 (float): Segundo número para la operación. + Returns: + int: Resultado de la operación + """ + # TODO: Realizar el desarrollo completo, incluida la documentación... recibe 2 números float y retorna la resta de ambos. + return num1 - num2 + + +def es_resultado_negativo(num1: float, num2: float) -> bool: + """ + Determina si el resultado de una operación entre num1 y num2 debe ser negativo. + + Args: + num1 (float): Primer numero para la comprobación + num2 (float): Segundo número para la comprobación + + Returns: + + bool: Si el resultado es negativo, devuelve True + + """ + + # TODO: Realizar el desarrollo completo de esta función teniendo en cuenta la documentación y completándola también. + # Debe pasar las pruebas unitarias. + # Se trata de una función que os puede venir bien para utilizarla tanto en la función multiplicar, como en dividir. + # Ya que va a determinar si la multiplicación o división entre dos números debería ser de signo negativo o no. + if num1 < 0 and num2 < 0: + negativo = False + elif num1 < 0 or num2 < 0: + negativo = True + else: + negativo = False + + return negativo + + +def multiplicar(num1: float, num2: float) -> int: + """ + Realiza la multiplicación ENTERA de dos números usando solo sumas y restas. + + Args: + num1 (float): Primer número. + num2 (float): Segundo número. + + Returns: + int: Resultado de la multiplicación. + + Note: + Debe redondear los números recibidos a enteros para trabajar. + """ + # TODO: + # 1. Realizar el desarrollo completo de esta función teniendo en cuenta la documentación. + # 2. Esta función debe pasar las pruebas unitarias. + # 3. No podéis usar el operador de multiplicación de Python para realizar el desarrollo de la misma, + # es decir, que debéis realizar la multiplicación con SUMAS y/o RESTAS... + # 4. Aunque se reciben números de tipo float, debéis redondearlos como números enteros para + # simplificar esta función, es decir, la operación 4.98 * 3.33, deberá convertirse en 5 * 3. + # 5. Tened en cuenta que podéis recibir números negativos, es decir, la operación -5 * -5 = 25 + # 6. OBLIGATORIO usar un bucle for. + # 7. Incluir algún comentario para mejorar la claridad y permitir que otros comprendan el propósito y funcionamiento del código. + comp1, comp2 = num1, num2 + num1 = int(round(num1)) + num2 = int(round(num2)) + + if num1 < 0: + num1 = -num1 + if num2 < 0: + num2 = -num2 + + resultado = 0 + for i in range(1, num1+1): + resultado += num2 + + if es_resultado_negativo(comp1, comp2) == True: + resultado = -resultado + + return int(resultado) + + + +def dividir(num1: float, num2: float) -> int: + """ + Realiza la división ENTERA de dos números usando solo sumas y restas. + + Args: + num1 (float): Dividendo. + num2 (float): Divisor. + + Returns: + int: Resultado de la división. + + Raises: + ZeroDivisionError: Si el divisor es cero. + + Note: + Debe redondear los números recibidos a enteros para trabajar. + """ + # TODO: + # 1. Realizar el desarrollo completo de esta función teniendo en cuenta la documentación. + # 2. Esta función debe pasar las pruebas unitarias. + # 3. No podéis usar el operador de división de Python para realizar el desarrollo de la misma, + # es decir, que debéis realizar la división con SUMAS y/o RESTAS... + # 4. Aunque se reciben números de tipo float, debéis redondearlos como números enteros para + # simplificar esta función, es decir, la operación 4.98 / 3.33, deberá convertirse en 5 / 3. + # 5. Tened en cuenta que podéis recibir números negativos, es decir, la operación -5 / -5 = 1 + # 6. Incluir algún comentario para mejorar la claridad y permitir que otros comprendan el propósito y funcionamiento del código. + + if num2 == 0: + raise ZeroDivisionError + + comp1, comp2 = num1, num2 + + num1 = round(num1) + num2 = round(num2) + + if num1 < 0: + num1 = -num1 + if num2 < 0: + num2 = -num2 + + contador = 0 + + while num1 >= num2: + contador = contador + 1 + num1 = num1 - num2 + if num2 == 0: + contador = 0 + + if es_resultado_negativo(comp1, comp2) == True: + contador = -contador + + return contador + + + + +def potencia(num1:float, num2:float): + # TODO: + # 1. Realizar el desarrollo completo de esta función y su documentación. + # 2. Esta función debe pasar las pruebas unitarias. + # 3. PREMISAS a tener en cuenta: + # - Cualquier número elevado a 0 da como resultado 1. + # - Para simplificar esta función, vamos a suponer que un número elevado a un + # exponente negativo siempre dará 0 (aunque en realidad no es así matemáticamente) + # 4. Utiliza la función de multiplicar para realizar los cálculos que te + # harán falta en esta función (RECUERDA que no puedes usar directamente los operadores + # de Python para la multiplicación y división). + # 5. Incluir algún comentario para mejorar la claridad y permitir que otros comprendan el propósito y funcionamiento del código. + + """ + Realiza una potencia de un número y su exponente gracias a la funcion multiplicar + + Args: + num1 (float): Base. + num2 (float): Exponente + + Returns: + int: Resultado de la potencia. + + """ + resultado = 1 + num1 = int(round(num1)) + num2 = int(round(num2)) + + if num2 == 1: + resultado = num1 + elif num2 == 0: + resultado = 1 + elif num2 < 0: + resultado = 0 + elif num2 != 0 and num2 > 0: + for _ in range(num2): + resultado = multiplicar(resultado, num1) + + return resultado + +def pedir_entrada(msj: str) -> str: + """ + Pide al usuario una entrada, elimina espacios por delante y por detrás y la convierte a minúsculas. + + Args: + msj (str): Mensaje para solicitar la entrada. + + Returns: + str: Entrada del usuario. + """ + # TODO: El desarrollo de esta función está incompleto, debéis terminarla teniendo en cuenta la documentación + mensaje = input(msj) + mensaje = mensaje.rstrip() + mensaje = mensaje.lstrip() + mensaje = mensaje.lower() + + return mensaje + + +def calcular_operacion(num1: float, num2: float, operador: str) -> float: + """ + Realiza la operación especificada entre num1 y num2 dependiendo del valor del operador. + + Args: + num1 (float): Primer número. + num2 (float): Segundo número. + operador (str): Operador de la operación. + + Returns: + float: Resultado de la operación. + + Raises: + ZeroDivisionError: Si el divisor es cero. + """ + # TODO: El desarrollo de esta función está incompleto... completadla teniendo en cuenta la documentación + # y que debe realizar las llamadas adecuadas a las funciones ya creadas para realizar los distintos + # cálculos... sumar, restar, multiplicar, dividir y potencia. + # IMPORTANTE: Si hacemos caso a la documentación de esta función, NO debéis capturar la excepción + # ZeroDivisionError aquí, sino que hay que dejadla que se propague a la función llamante, realizar_calculo(), + # dónde se os indica cómo realizar la gestión de estos errores. + resultado = 0 + + if operador == '+': + resultado = sumar(num1, num2) + elif operador == '-': + resultado = restar(num1, num2) + elif operador == 'x': + resultado = multiplicar(num1, num2) + elif operador == '*': + resultado = multiplicar(num1, num2) + elif operador == ':': + resultado = dividir(num1, num2) + elif operador == '/': + resultado = dividir(num1, num2) + elif operador == '**': + resultado = potencia(num1, num2) + elif operador == 'exp': + resultado = potencia(num1, num2) + + if resultado == None: + resultado = 0 + + resultado = float(resultado) + + return round(resultado, 2) + + + +def obtener_operaciones() -> str: + """ + Devuelve una cadena con la lista de operaciones disponibles en la calculadora. + + Returns: + (str): cadena de caracteres con la información de las operaciones disponibles. + """ + # TODO: El desarrollo de esta función está incompleto... ver documentación para solucionarla correctamente. + """ + Operaciones disponibles: + ce => Reiniciar resultado a 0 + decimales => Establecer decimales en resultado + cadena vacía + => Pregunta si desea salir + calculo => Iniciar cálculo secuencial + + => Suma + - => Resta + x o * => Multiplicación + / o : => División + ** o exp => Potencia + cancelar => vovler sin actualizar resultado de la calculadora + cadena vacía + => volver actualizando resultado de la calculadora + """ + lista = f'\nOperaciones disponibles:\n ce => Reiniciar el resultado a 0\n decimales => Establecer decimales en resultado\n cadena vacía + => Pregunta si desea salir\n calculo => Iniciar cálculo secuencial\n + => Suma\n - => Resta\n x o * => Multiplicación\n / o : => División\n ** o exp => Potencia\n cancelar => volver sin actualizar resultado de la calculadora\n cadena vacía + => volver actualizando resultado de la calculadora\n' + + return lista + +def realizar_calculo(decimales: int, resultado_almacenado: float): + """ + Realiza una secuencia de cálculos solicitando números y operadores al usuario. + + Args: + decimales (int): Número de decimales para el resultado. + resultado_almacenado (float): Valor almacenado en la calculadora. + + Returns: + float: Resultado final del cálculo o None si se cancela. + + Note: + * Dentro de esta función el usuario puede realizar cálculos secuenciales, es decir, + comenzará introduciendo un número, después un operador, y otro número... a partir + de aquí sobre el resultado acumulado, introducirá operador y número para seguir + realizando cálculos (ver ejemplos en README.md de la tarea en el repositorio de GitHub). + * El usuario es guiado para introducir números y operadores secuencialmente + para realizar operaciones básicas. + * El usuario puede utilizar "resultado" en la secuencia de cálculo para reutilizar el + resultado almacenado en la calculadora. + * El cálculo finaliza al pulsar , volviendo y actualizando el resultado almacenado + de la calculadora con el cálculo realizado. + * También podemos escribir "cancelar", volviendo sin realizar ningún cambio en el + resultado almacenado de la calculadora. + """ + # TODO: El desarrollo de esta función está incompleto... ver documentación para solucionarla correctamente. + + operador = None + resultado = None + realizando_calculos = True + + print("\n## Ingrese número, operador, 'resultado', 'cancelar' o para finalizar el cálculo ##\n") + + while realizando_calculos: + entrada = pedir_entrada(f"\t (Cálculo = {resultado}) >> ") + + if entrada == "cancelar": + realizando_calculos = False + limpiar_pantalla() + + elif entrada == "": + realizando_calculos = False + resultado_almacenado = resultado + limpiar_pantalla() + + + elif entrada in OPERADORES: + operador = entrada + + else: + # TODO: este código funciona cuando solucionéis que reconozca las variables resultado_almacenado y decimales. + # Pero no gestiona los posibles tipos de Excepciones que se pueden producir: + # - ValueError que debe mostrar el error que está en la posición 2 de MENSAJES_ERROR. + # - ZeroDivisionError que debe mostrar el error que está en la posición 5 de MENSAJES_ERROR. + # - Exception que debe mostrar el error que está en la posición 6 de MENSAJES_ERROR. + if entrada == "resultado": + entrada = resultado_almacenado + + try: + numero = float(entrada) + + if operador is not None: + if resultado is None: + resultado = 0 + resultado = round(calcular_operacion(resultado, numero, operador), decimales) + operador = None + + elif resultado is None: + resultado = numero + + else: + mostrar_error(3) + except ValueError: + mostrar_error(2) + except ZeroDivisionError: + mostrar_error(5) + pausa() + except Exception as e: + mostrar_error(6, e) + pausa() + except TypeError as e: + mostrar_error(2, e) + pausa() + + return resultado_almacenado + + +def main(): + """ + Función principal de la calculadora. Gestiona la entrada del usuario y coordina las operaciones. + + Note: + El flujo del programa es el siguiente: + + 1. Inicia la calculadora mostrando el resultado almacenado por defecto (0.00). + + 2. El usuario ingresa un comando, que puede ser: + - "lista" para ver todas las operaciones disponibles. + - "ce" para reiniciar el resultado almacenado a 0. + - "decimales " para establecer el número de decimales mostrados en el resultado. + - "calculo" para iniciar una secuencia de cálculo paso a paso. + - Una entrada vacía y pulsa la tecla para salir de la calculadora. + + 3. Según el comando ingresado: + - El programa realiza la operación o ejecuta la acción indicada. + - Al ingresar "calculo": + * El usuario es guiado para introducir números y operadores secuencialmente para realizar operaciones básicas. + * El usuario puede utilizar "resultado" en la secuencia de cálculo para reutilizar el resultado almacenado en la calculadora. + * El cálculo finaliza al pulsar , volviendo y actualizando el resultado almacenado de la calculadora con el cálculo realizado. + * También podemos escribir "cancelar", volviendo sin realizar ningún cambio en el resultado almacenado de la calculadora. + + 4. La calculadora sigue ejecutándose hasta que el usuario confirma la salida al ingresar una entrada vacía y pulsar . + + 5. Finalmente, se limpia la pantalla, el programa se despide con el mensaje "\n\nBye, bye...\n\n" y termina. + """ + # TODO: Corrige los errores y haz que el main funcione correctamente... + + decimales = 1 + resultado = 0.0 + desea_salir = False + entrada = '' + + limpiar_pantalla() + + while not desea_salir: + + entrada = pedir_entrada(f"Operación (RES => {resultado}) >> ") + obtener_operaciones() + + if entrada == "": + salir = pedir_entrada("¿Desea salir de la calculadora? (s/n) ").lower() + if salir == 's': + desea_salir = True + limpiar_pantalla() + print('\n\n\n\n Bye, bye...\n\n\n\n') + else: + limpiar_pantalla() + + elif entrada == "lista": + print(obtener_operaciones()) + + elif entrada == "ce": + resultado = 0 + + elif entrada.startswith("decimales"): + # Extraemos las posiciones decimales y las convertimos a un valor entero + try: + decimales = str(entrada.split()[1]) + print(f"\nDecimales configurados a {decimales}.\n") + except IndexError as e: + mostrar_error(6, e) + + + elif entrada == "calculo": + limpiar_pantalla() + try: + calculo = realizar_calculo(decimales, resultado) + resultado += calculo + except TypeError: + pass + resultado = 0 + + # else: + # mostrar_error() + +if __name__ == '__main__': + main() \ No newline at end of file diff --git a/dam1_calculadora/jaime_calculadora_alumnos.py b/dam1_calculadora/jaime_calculadora_alumnos.py new file mode 100644 index 0000000..4620cab --- /dev/null +++ b/dam1_calculadora/jaime_calculadora_alumnos.py @@ -0,0 +1,480 @@ +import os +import time +# TODO: ATENCIÓN! +# 1. Importar los paquetes necesarios +# 2. Usar la función mostrar_error para imprimir los errores por consola. +# 3. Los comentarios TODO os ayudan a resolver cada apartado de esta prueba. +# 4. CADA función SOLO puede tener una instrucción return. +# 5. En test_calculadora_alumnos.py crear el test unitario para la función es_resultado_negativo y hacer que todos los test unitarios se cumplan. + + + +# Mensajes de error predefinidos +MENSAJES_ERROR = ( + "Problemas al intentar limpiar la pantalla {error}", + "Error al configurar los decimales. Formato: decimales .", + "Entrada no válida. Ingrese número, operador, 'resultado', 'cancelar' o para finalizar el cálculo.", + "Error: Introduzca un operador antes de otro número.", + "Comando no reconocido. Escriba 'lista' para ver las operaciones disponibles.", + "Error: no es posible la división por 0! Introduzca otro valor diferente a 0...", + "Se produjo un error: {error}" +) + +# Operadores soportados por la calculadora +OPERADORES = "+", "-","*","/","**" +# TODO: Ayuda: incluye en esta constante los símbolos que reconocerá la aplicación para el cálculo de las operaciones: +# '+' para la suma. +# '-' para la resta. +# 'x' o '*' para la multiplicación. +# '/' o ':' para la división. +# '**' o 'exp' para la potencia. + + + +def limpiar_pantalla(): + """ + Limpia la consola según el sistema operativo. + """ + # TODO: El desarrollo de esta función está incompleto y con errores. + try: + if os.name == "posix": + os.system(clear) + else: + os.system(cls) + if os.name != "posix" and os.name != "nt" : + raise ValueError (MENSAJES_ERROR(1)) + except Exception as e: + print(f"error {e}") + + + + +def pausa(tiempo = 0, tecla_enter = False): + """ + Pausa la ejecución del programa hasta que se pulse ENTER... "\nPresione ENTER para continuar..." + """ + # TODO: Desarrollar esta función según su documentación. + + if tiempo > 0 : + time.sleep(tiempo) + + elif tecla_enter: + input("\nPresione ENTER para continuar...") + + +def mostrar_error(indice_error: int, msj_error = None): + """ + Muestra un mensaje de error en la consola. + + Args: + indice_error (int): Índice del mensaje de error en MENSAJES_ERROR. + msj_error (str, opcional): Texto adicional para personalizar el mensaje de error. + """ + # TODO: + # 1. Corrige el error que existe a la hora de acceder a los mensajes de error que están en la constante + # MENSAJES_ERROR. A los elementos de una tupla se accede igual que a los caracteres de una + # cadena de caracteres. + # 2. Completa el código de esta función para que controle específicamente las excepciones IndexError y + # muestre el mensaje: "\n*ERROR* Mensaje de error no definido.\n" + # 3. También se pide que se controle cualquier otra excepción que se pueda producir y muestre el mensaje: + # "\n*ERROR* Problemas al mostrar error!\n{e}\n" + # 4. En esta función los mensajes de error deben mostrarse con print. + if msj_error != None: + print(f"\n*ERROR* {MENSAJES_ERROR.format(error = msj_error)}\n") + else: + print(f"\n*ERROR* {MENSAJES_ERROR}\n") + + +def sumar(num1 = float, num2 = float) -> float: + # TODO: Realizar el desarrollo completo, incluida la documentación... recibe 2 números float y retorna la suma de ambos. + """ +Recibe dos numeros en float que son inputs de otra función y retorna el resultado de la operación suma. + + """ + suma = num1 + num2 + + return suma + + +def restar(num1 = float, num2 = float) -> float: + # TODO: Realizar el desarrollo completo, incluida la documentación... recibe 2 números float y retorna la resta de ambos. + """ + Recibe dos numeros en float que son inputs de otra función y retorna el resultado de la operación suma. + + """ + + resta = num1 - num2 + + return resta + + +def es_resultado_negativo(num1: float, num2: float) -> bool: + """Determina si el resultado de una operación entre num1 y num2 debe ser negativo. + Si el resultado es de una multiplicación o división en la que los signos sean iguales + se concatena el signo positivo, si no se concatena el signo negativo. + """ + # TODO: Realizar el desarrollo completo de esta función teniendo en cuenta la documentación y completándola también. + # Debe pasar las pruebas unitarias. + # Se trata de una función que os puede venir bien para utilizarla tanto en la función multiplicar, como en dividir. + # Ya que va a determinar si la multiplicación o división entre dos números debería ser de signo negativo o no. + resultado = 0 + + if num1 < 0 and num2 > 0 or num2 > 0 and num1 < 0 : + resultado += "-" + elif num1 < 0 and num2 < 0 : + resultado += "+" + else : + resultado += "+" + + return resultado + + +def multiplicar(num1: float, num2: float ) -> int : + """ + Realiza la multiplicación ENTERA de dos números usando solo sumas y restas. + + Args: + num1 (float): Primer número. + num2 (float): Segundo número. + + Returns: + int: Resultado de la multiplicación. + + Note: + Debe redondear los números recibidos a enteros para trabajar. + """ + multiplicacion = 0 + numero_1 = round(num1) + numero_2 = round(num2) + for i in range(1,numero_2 + 1): #de esta manera estamos sumando num1 las veces que num2 pide que en si es el significado de la mmultiplicación. + multiplicacion += numero_1 + + return print(es_resultado_negativo() + multiplicacion) + + # TODO: + # 1. Realizar el desarrollo completo de esta función teniendo en cuenta la documentación. + # 2. Esta función debe pasar las pruebas unitarias. + # 3. No podéis usar el operador de multiplicación de Python para realizar el desarrollo de la misma, + # es decir, que debéis realizar la multiplicación con SUMAS y/o RESTAS... + # 4. Aunque se reciben números de tipo float, debéis redondearlos como números enteros para + # simplificar esta función, es decir, la operación 4.98 * 3.33, deberá convertirse en 5 * 3. + # 5. Tened en cuenta que podéis recibir números negativos, es decir, la operación -5 * -5 = 25 + # 6. OBLIGATORIO usar un bucle for. + # 7. Incluir algún comentario para mejorar la claridad y permitir que otros comprendan el propósito y funcionamiento del código. + + + +def dividir(num1: float, num2: float) -> int: + """ + Realiza la división ENTERA de dos números usando solo sumas y restas. + + Args: + num1 (float): Dividendo. + num2 (float): Divisor. + + Returns: + int: Resultado de la división. + + Raises: + ZeroDivisionError: Si el divisor es cero. + + Note: + Debe redondear los números recibidos a enteros para trabajar. + """ + division_correcta =None + while not division_correcta: + try: + + cociente = 0 + dividendo = round(num1) + divisor = round(num2) + division= 0 + while dividendo != 0: + + division+= dividendo - divisor + + cociente+= 1 + + if divisor == 0 : + raise ZeroDivisionError(MENSAJES_ERROR(6)) + + return print(es_resultado_negativo()+cociente) + except ValueError as e : + print (f"ERROR {e}") + + # TODO: + # 1. Realizar el desarrollo completo de esta función teniendo en cuenta la documentación. + # 2. Esta función debe pasar las pruebas unitarias. + # 3. No podéis usar el operador de división de Python para realizar el desarrollo de la misma, + # es decir, que debéis realizar la división con SUMAS y/o RESTAS... + # 4. Aunque se reciben números de tipo float, debéis redondearlos como números enteros para + # simplificar esta función, es decir, la operación 4.98 / 3.33, deberá convertirse en 5 / 3. + # 5. Tened en cuenta que podéis recibir números negativos, es decir, la operación -5 / -5 = 1 + # 6. Incluir algún comentario para mejorar la claridad y permitir que otros comprendan el propósito y funcionamiento del código. + + +def potencia(num: float,exp:float ) -> int: + # TODO: + # 1. Realizar el desarrollo completo de esta función y su documentación. + # 2. Esta función debe pasar las pruebas unitarias. + # 3. PREMISAS a tener en cuenta: + # - Cualquier número elevado a 0 da como resultado 1. + # - Para simplificar esta función, vamos a suponer que un número elevado a un + # exponente negativo siempre dará 0 (aunque en realidad no es así matemáticamente) + # 4. Utiliza la función de multiplicar para realizar los cálculos que te + # harán falta en esta función (RECUERDA que no puedes usar directamente los operadores + # de Python para la multiplicación y división). + # 5. Incluir algún comentario para mejorar la claridad y permitir que otros comprendan el propósito y funcionamiento del código. + numero= round(num) + exponente= round(exp) + num_elevado = 0 + if exponente != 0 : + for i in range (1, exponente + 1) :#la potencia es la suma de las muliplicaciones + num_elevado += multiplicar(numero) + else: + num_elevado = 1 + + return num_elevado +def pedir_entrada(msj: str) -> str: + """ + Pide al usuario una entrada, elimina espacios por delante y por detrás y la convierte a minúsculas. + + Args: + msj (str): Mensaje para solicitar la entrada. + + Returns: + str: Entrada del usuario. + """ + # TODO: El desarrollo de esta función está incompleto, debéis terminarla teniendo en cuenta la documentación + msj= input("ingrese número para operar: ").strip() + mensaje = msj + + return mensaje + + +def calcular_operacion(num1: float, num2: float, operador: str) -> float: + """ + Realiza la operación especificada entre num1 y num2 dependiendo del valor del operador. + + Args: + num1 (float): Primer número. + num2 (float): Segundo número. + operador (str): Operador de la operación. + + Returns: + float: Resultado de la operación. + + Raises: + ZeroDivisionError: Si el divisor es cero. + """ + numero1=num1 + numero2=num2 + + suma= OPERADORES(1) + resta=OPERADORES(2) + multiplica= OPERADORES(3) + dividi= OPERADORES(4) + potenci = OPERADORES(5) + operador= input(" ingresa un operador") + operado = operador + if operado == suma: + resultado = sumar(numero1,numero2) + elif operado == resta : + resultado = resta(numero1,numero2) + elif operado ==multiplica : + resultado = multiplicar(numero1,numero2) + elif operado == dividi: + resultado = dividir(numero1,numero2) + elif operado == potenci: + resultado =potencia(numero1,numero2) + # TODO: El desarrollo de esta función está incompleto... completadla teniendo en cuenta la documentación + # y que debe realizar las llamadas adecuadas a las funciones ya creadas para realizar los distintos + # cálculos... sumar, restar, multiplicar, dividir y potencia. + # IMPORTANTE: Si hacemos caso a la documentación de esta función, NO debéis capturar la excepción + # ZeroDivisionError aquí, sino que hay que dejadla que se propague a la función llamante, realizar_calculo(), + # dónde se os indica cómo realizar la gestión de estos errores. + + return resultado + + + +def obtener_operaciones() -> str: + """ + Devuelve una cadena con la lista de operaciones disponibles en la calculadora. + + Returns: + (str): cadena de caracteres con la información de las operaciones disponibles. + """ + # TODO: El desarrollo de esta función está incompleto... ver documentación para solucionarla correctamente. + """ + Operaciones disponibles: + ce => Reiniciar resultado a 0 + decimales => Establecer decimales en resultado + cadena vacía + => Pregunta si desea salir + calculo => Iniciar cálculo secuencial + + => Suma + - => Resta + x o * => Multiplicación + / o : => División + ** o exp => Potencia + cancelar => vovler sin actualizar resultado de la calculadora + cadena vacía + => volver actualizando resultado de la calculadora + """ + resultado = 0.00 + decimales = 2 + + while True: + realizar_calculo(resultado, decimales) + comando = input().strip() + + if comando == "lista": + print("Operaciones disponibles:") + print(" ce => Reiniciar resultado a 0") + print(" decimales => Establecer decimales en resultado") + print(" cadena vacía + => Pregunta si desea salir") + print(" calculo => Iniciar cálculo secuencial") + print(" + => Suma") + print(" - => Resta") + print(" x o * => Multiplicación") + print(" / o : => División") + print(" ** o exp => Potencia") + print(" cancelar => volver sin actualizar resultado de la calculadora") + print(" cadena vacía + => volver actualizando resultado de la calculadora") + + +def realizar_calculo(): + """ + Realiza una secuencia de cálculos solicitando números y operadores al usuario. + + Args: + decimales (int): Número de decimales para el resultado. + resultado_almacenado (float): Valor almacenado en la calculadora. + + Returns: + float: Resultado final del cálculo o None si se cancela. + + Note: + * Dentro de esta función el usuario puede realizar cálculos secuenciales, es decir, + comenzará introduciendo un número, después un operador, y otro número... a partir + de aquí sobre el resultado acumulado, introducirá operador y número para seguir + realizando cálculos (ver ejemplos en README.md de la tarea en el repositorio de GitHub). + * El usuario es guiado para introducir números y operadores secuencialmente + para realizar operaciones básicas. + * El usuario puede utilizar "resultado" en la secuencia de cálculo para reutilizar el + resultado almacenado en la calculadora. + * El cálculo finaliza al pulsar , volviendo y actualizando el resultado almacenado + de la calculadora con el cálculo realizado. + * También podemos escribir "cancelar", volviendo sin realizar ningún cambio en el + resultado almacenado de la calculadora. + """ + # TODO: El desarrollo de esta función está incompleto... ver documentación para solucionarla correctamente. + + operador = None + resultado = None + realizando_calculos = True + + print("\n## Ingrese número, operador, 'resultado', 'cancelar' o para finalizar el cálculo ##\n") + + while realizando_calculos: + entrada = pedir_entrada(f"\t (Cálculo = resultado) >> ") + + if entrada == "cancelar": + limpiar_pantalla + + + elif entrada == "": + limpiar_pantalla() + + + elif entrada in OPERADORES: + operador = OPERADORES() + + else: + # TODO: este código funciona cuando solucionéis que reconozca las variables resultado_almacenado y decimales. + # Pero no gestiona los posibles tipos de Excepciones que se pueden producir: + # - ValueError que debe mostrar el error que está en la posición 2 de MENSAJES_ERROR. + # - ZeroDivisionError que debe mostrar el error que está en la posición 5 de MENSAJES_ERROR. + # - Exception que debe mostrar el error que está en la posición 6 de MENSAJES_ERROR. + resultado_almacenado = 0 + if entrada == "resultado": + entrada = resultado_almacenado + + try: + numero = float(entrada) + + if operador is not None: + if resultado is None: + resultado = 0 + resultado = round(calcular_operacion(resultado, numero, operador)) + operador = None + + elif resultado is None: + resultado = numero + + else: + mostrar_error(3) + except ValueError as e : + print(MENSAJES_ERROR(2)) + + +def main(): + """ + Función principal de la calculadora. Gestiona la entrada del usuario y coordina las operaciones. + + Note: + El flujo del programa es el siguiente: + + 1. Inicia la calculadora mostrando el resultado almacenado por defecto (0.00). + + 2. El usuario ingresa un comando, que puede ser: + - "lista" para ver todas las operaciones disponibles. + - "ce" para reiniciar el resultado almacenado a 0. + - "decimales " para establecer el número de decimales mostrados en el resultado. + - "calculo" para iniciar una secuencia de cálculo paso a paso. + - Una entrada vacía y pulsa la tecla para salir de la calculadora. + + 3. Según el comando ingresado: + - El programa realiza la operación o ejecuta la acción indicada. + - Al ingresar "calculo": + * El usuario es guiado para introducir números y operadores secuencialmente para realizar operaciones básicas. + * El usuario puede utilizar "resultado" en la secuencia de cálculo para reutilizar el resultado almacenado en la calculadora. + * El cálculo finaliza al pulsar , volviendo y actualizando el resultado almacenado de la calculadora con el cálculo realizado. + * También podemos escribir "cancelar", volviendo sin realizar ningún cambio en el resultado almacenado de la calculadora. + + 4. La calculadora sigue ejecutándose hasta que el usuario confirma la salida al ingresar una entrada vacía y pulsar . + + 5. Finalmente, se limpia la pantalla, el programa se despide con el mensaje "\n\nBye, bye...\n\n" y termina. + """ + # TODO: Corrige los errores y haz que el main funcione correctamente... + + decimales = 2 + resultado = 0.0 + desea_salir= None + + while not desea_salir: + + entrada=pedir_entrada(f"Operación (RES => resultado) >> ") + + if entrada == "": + pedir_entrada("¿Desea salir de la calculadora? (s/n) ") + + elif entrada == "lista": + obtener_operaciones + + elif entrada == "ce": + resultado = 0.0 + + + elif entrada.startswith("decimales"): + # Extraemos las posiciones decimales y las convertimos a un valor entero + decimales = str(entrada.split()[1]) + print(f"Decimales configurados a {decimales}.") + + elif entrada == "calculo": + realizar_calculo(decimales, resultado) + + else: + mostrar_error + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/dam1_calculadora/jesus_calculadora_alumnos.py b/dam1_calculadora/jesus_calculadora_alumnos.py new file mode 100644 index 0000000..8287b9d --- /dev/null +++ b/dam1_calculadora/jesus_calculadora_alumnos.py @@ -0,0 +1,450 @@ +# TODO: ATENCIÓN!!! +# 1. Importar los paquetes necesarios +# 2. Usar la función mostrar_error para imprimir los errores por consola. +# 3. Los comentarios TODO os ayudan a resolver cada apartado de esta prueba. +# 4. CADA función SOLO puede tener una instrucción return. +# 5. En test_calculadora_alumnos.py crear el test unitario para la función es_resultado_negativo y hacer que todos los test unitarios se cumplan. + + + +# Mensajes de error predefinidos +import os + + +MENSAJES_ERROR = ( + "Problemas al intentar limpiar la pantalla {error}", + "Error al configurar los decimales. Formato: decimales .", + "Entrada no válida. Ingrese número, operador, 'resultado', 'cancelar' o para finalizar el cálculo.", + "Error: Introduzca un operador antes de otro número.", + "Comando no reconocido. Escriba 'lista' para ver las operaciones disponibles.", + "Error: no es posible la división por 0! Introduzca otro valor diferente a 0...", + "Se produjo un error: {error}" +) + +# Operadores soportados por la calculadora +OPERADORES = "+","-","x","*","/",":","**","exp" +# TODO: Ayuda: incluye en esta constante los símbolos que reconocerá la aplicación para el cálculo de las operaciones: +# '+' para la suma. +# '-' para la resta. +# 'x' o '*' para la multiplicación. +# '/' o ':' para la división. +# '**' o 'exp' para la potencia. + +COMANDOS_DISPONIBLES = "lista","ce","decimales","calculo","" + +def limpiar_pantalla(): + """ + Limpia la consola según el sistema operativo. + """ + # TODO: El desarrollo de esta función está incompleto y con errores. + try: + #os.system(clear if os.name = posix else cls) + if os.name == "posix": + os.system("clear") + else: + os.system("cls") + # Otra forma de expresar la misma instrucción sería la siguiente: + # if os.name = posix: + # os.system(clear) + # else: + # os.system(cls) + except Exception as e: + mostrar_error([0]) + + +def pausa(tecla_enter = False): + """ + Pausa la ejecución del programa hasta que se pulse ENTER... "\nPresione ENTER para continuar..." + """ + # TODO: Desarrollar esta función según su documentación. + if tecla_enter == True: + input("\nPresione ENTER para continuar...") + +def mostrar_error(indice_error: int, msj_error = None): + """ + Muestra un mensaje de error en la consola. + + Args: + indice_error (int): Índice del mensaje de error en MENSAJES_ERROR. + msj_error (str, opcional): Texto adicional para personalizar el mensaje de error. + """ + # TODO: + # 1. Corrige el error que existe a la hora de acceder a los mensajes de error que están en la constante + # MENSAJES_ERROR. A los elementos de una tupla se accede igual que a los caracteres de una + # cadena de caracteres. + # 2. Completa el código de esta función para que controle específicamente las excepciones IndexError y + # muestre el mensaje: "\n*ERROR* Mensaje de error no definido.\n" + # 3. También se pide que se controle cualquier otra excepción que se pueda producir y muestre el mensaje: + # "\n*ERROR* Problemas al mostrar error!\n{e}\n" + # 4. En esta función los mensajes de error deben mostrarse con print. + if msj_error != None: + print(f"\n*ERROR* {MENSAJES_ERROR.format(error = msj_error)}\n") + else: + print(f"\n*ERROR* {MENSAJES_ERROR}\n") + + +def sumar(num1: float, num2: float): + # TODO: Realizar el desarrollo completo, incluida la documentación... recibe 2 números float y retorna la suma de ambos. + """ + Recibe dos números, los suma y los retorna. + Args: + num1: primer número introducido por el usuario. + num2: segundo número introducido por el usuario. + Returns: + La suma de los dos números. + """ + suma = num1 + num2 + return suma + + + +def restar(num1: float, num2: float)->float: + resta = num1 - num2 + return resta + + # TODO: Realizar el desarrollo completo, incluida la documentación... recibe 2 números float y retorna la resta de ambos. + + + +def es_resultado_negativo(resultado: int) -> bool: + """Determina si el resultado de una operación entre num1 y num2 debe ser negativo.""" + # TODO: Realizar el desarrollo completo de esta función teniendo en cuenta la documentación y completándola también. + # Debe pasar las pruebas unitarias. + # Se trata de una función que os puede venir bien para utilizarla tanto en la función multiplicar, como en dividir. + # Ya que va a determinar si la multiplicación o división entre dos números debería ser de signo negativo o no. + if len(str(resultado)) == "-": + return True + + elif len(str(resultado)) == "-": + return True + else: + return False + + + + +def multiplicar(num1: float, num2: float)->int: + """ + Realiza la multiplicación ENTERA de dos números usando solo sumas y restas. + + Args: + num1 (float): Primer número. + num2 (float): Segundo número. + + Returns: + int: Resultado de la multiplicación. + + Note: + Debe redondear los números recibidos a enteros para trabajar. + """ + # TODO: + # 1. Realizar el desarrollo completo de esta función teniendo en cuenta la documentación. + # 2. Esta función debe pasar las pruebas unitarias. + # 3. No podéis usar el operador de multiplicación de Python para realizar el desarrollo de la misma, + # es decir, que debéis realizar la multiplicación con SUMAS y/o RESTAS... + # 4. Aunque se reciben números de tipo float, debéis redondearlos como números enteros para + # simplificar esta función, es decir, la operación 4.98 * 3.33, deberá convertirse en 5 * 3. + # 5. Tened en cuenta que podéis recibir números negativos, es decir, la operación -5 * -5 = 25 + # 6. OBLIGATORIO usar un bucle for. + # 7. Incluir algún comentario para mejorar la claridad y permitir que otros comprendan el propósito y funcionamiento del código. + num1 = str(num1.split()[0]) + num1 = int(num1) + num2 = str(num2.split()[0]) + num2 = int(num2) + for i in range(num2): + multiplicacion = sumar(num1,num1) + resultado += multiplicacion + if es_resultado_negativo(resultado): + resultado = 0 - resultado + else: + return resultado + + + +def dividir(): + """ + Realiza la división ENTERA de dos números usando solo sumas y restas. + + Args: + num1 (float): Dividendo. + num2 (float): Divisor. + + Returns: + int: Resultado de la división. + + Raises: + ZeroDivisionError: Si el divisor es cero. + + Note: + Debe redondear los números recibidos a enteros para trabajar. + """ + # TODO: + # 1. Realizar el desarrollo completo de esta función teniendo en cuenta la documentación. + # 2. Esta función debe pasar las pruebas unitarias. + # 3. No podéis usar el operador de división de Python para realizar el desarrollo de la misma, + # es decir, que debéis realizar la división con SUMAS y/o RESTAS... + # 4. Aunque se reciben números de tipo float, debéis redondearlos como números enteros para + # simplificar esta función, es decir, la operación 4.98 / 3.33, deberá convertirse en 5 / 3. + # 5. Tened en cuenta que podéis recibir números negativos, es decir, la operación -5 / -5 = 1 + # 6. Incluir algún comentario para mejorar la claridad y permitir que otros comprendan el propósito y funcionamiento del código. + num1 = str(num1.split()[0]) + num1 = int(num1) + num2 = str(num2.split()[0]) + num2 = int(num2) + while num1 > 1: + division = num1 - num1 + resultado += division + if es_resultado_negativo(resultado): + resultado = 0 - resultado + else: + return resultado + return resultado + + +#def potencia(): + # TODO: + # 1. Realizar el desarrollo completo de esta función y su documentación. + # 2. Esta función debe pasar las pruebas unitarias. + # 3. PREMISAS a tener en cuenta: + # - Cualquier número elevado a 0 da como resultado 1. + # - Para simplificar esta función, vamos a suponer que un número elevado a un + # exponente negativo siempre dará 0 (aunque en realidad no es así matemáticamente) + # 4. Utiliza la función de multiplicar para realizar los cálculos que te + # harán falta en esta función (RECUERDA que no puedes usar directamente los operadores + # de Python para la multiplicación y división). + # 5. Incluir algún comentario para mejorar la claridad y permitir que otros comprendan el propósito y funcionamiento del código. + + +def pedir_entrada(msj: str) -> str: + """ + Pide al usuario una entrada, elimina espacios por delante y por detrás y la convierte a minúsculas. + + Args: + msj (str): Mensaje para solicitar la entrada. + + Returns: + str: Entrada del usuario. + """ + entrada_correcta = False + while not entrada_correcta: + try: + entrada = input(msj) + entrada = entrada.lower() + entrada = entrada.strip() + entrada_correcta = True + except ValueError as e: + mostrar_error(e) + + + + + + # TODO: El desarrollo de esta función está incompleto, debéis terminarla teniendo en cuenta la documentación + return entrada + + +def calcular_operacion(num1: float, num2: float, operador: str) -> float: + + """ + Realiza la operación especificada entre num1 y num2 dependiendo del valor del operador. + + Args: + num1 (float): Primer número. + num2 (float): Segundo número. + operador (str): Operador de la operación. + + Returns: + float: Resultado de la operación. + + Raises: + ZeroDivisionError: Si el divisor es cero. + """ + # TODO: El desarrollo de esta función está incompleto... completadla teniendo en cuenta la documentación + # y que debe realizar las llamadas adecuadas a las funciones ya creadas para realizar los distintos + # cálculos... sumar, restar, multiplicar, dividir y potencia. + # IMPORTANTE: Si hacemos caso a la documentación de esta función, NO debéis capturar la excepción + # ZeroDivisionError aquí, sino que hay que dejadla que se propague a la función llamante, realizar_calculo(), + # dónde se os indica cómo realizar la gestión de estos errores. + OPERADORES = "+","-","x","*","/",":","**","exp" + if operador == OPERADORES[0]: + resultado = sumar(num1,num2) + elif operador == OPERADORES[1]: + resultado = restar(num1,num2) + elif operador == OPERADORES[2] or operador == OPERADORES[3]: + resultado = multiplicar(num1,num2) + elif operador == OPERADORES[4] or operador == OPERADORES[5]: + resultado = dividir(num1,num2) + elif operador == OPERADORES[6] or operador == OPERADORES[7]: + resultado = potencia(num1,num2) + return resultado + + + + +def obtener_operaciones() -> str: + """ + Devuelve una cadena con la lista de operaciones disponibles en la calculadora. + + Returns: + (str): cadena de caracteres con la información de las operaciones disponibles. + """ + # TODO: El desarrollo de esta función está incompleto... ver documentación para solucionarla correctamente. + """ + Operaciones disponibles: + ce => Reiniciar resultado a 0 + decimales => Establecer decimales en resultado + cadena vacía + => Pregunta si desea salir + calculo => Iniciar cálculo secuencial + + => Suma + - => Resta + x o * => Multiplicación + / o : => División + ** o exp => Potencia + cancelar => vovler sin actualizar resultado de la calculadora + cadena vacía + => volver actualizando resultado de la calculadora + """ + operaciones_disponibles = f"Operaciones disponibles:\n ce => Reiniciar resultado a 0\n cadena vacía + => Pregunta si desea salir\n calculo => Iniciar cálculo secuencial\n + => Suma\n - => Resta\n x o * => Multiplicación\n / o : => División\n ** o exp => Potencia\n cancelar => vovler sin actualizar resultado de la calculadora\n cadena vacía + => volver actualizando resultado de la calculadora " + return operaciones_disponibles + + +def realizar_calculo(entrada :str, decimales:int): + """ + Realiza una secuencia de cálculos solicitando números y operadores al usuario. + + Args: + decimales (int): Número de decimales para el resultado. + resultado_almacenado (float): Valor almacenado en la calculadora. + + Returns: + float: Resultado final del cálculo o None si se cancela. + + Note: + * Dentro de esta función el usuario puede realizar cálculos secuenciales, es decir, + comenzará introduciendo un número, después un operador, y otro número... a partir + de aquí sobre el resultado acumulado, introducirá operador y número para seguir + realizando cálculos (ver ejemplos en README.md de la tarea en el repositorio de GitHub). + * El usuario es guiado para introducir números y operadores secuencialmente + para realizar operaciones básicas. + * El usuario puede utilizar "resultado" en la secuencia de cálculo para reutilizar el + resultado almacenado en la calculadora. + * El cálculo finaliza al pulsar , volviendo y actualizando el resultado almacenado + de la calculadora con el cálculo realizado. + * También podemos escribir "cancelar", volviendo sin realizar ningún cambio en el + resultado almacenado de la calculadora. + """ + # TODO: El desarrollo de esta función está incompleto... ver documentación para solucionarla correctamente. + resultado = None + operador = None + realizando_calculos = True + resultado_almacenado = 0.00 + print("\n## Ingrese número, operador, 'resultado', 'cancelar' o para finalizar el cálculo ##\n") + + while realizando_calculos: + entrada = pedir_entrada(f"\t (Cálculo = {resultado_almacenado:{decimales}f}) >> ") + + if entrada == "cancelar": + resultado = resultado_almacenado + + + + else: + + # TODO: este código funciona cuando solucionéis que reconozca las variables resultado_almacenado y decimales. + # Pero no gestiona los posibles tipos de Excepciones que se pueden producir: + # - ValueError que debe mostrar el error que está en la posición 2 de MENSAJES_ERROR. + # - ZeroDivisionError que debe mostrar el error que está en la posición 5 de MENSAJES_ERROR. + # - Exception que debe mostrar el error que está en la posición 6 de MENSAJES_ERROR. + if entrada == "resultado": + entrada = resultado_almacenado + + try: + numero = entrada + if numero.isdigit(): + num1 = int(numero) + num2 = int(numero) + + else: + operador = entrada + if operador is not None: + if resultado is None: + resultado = 0 + resultado = round(calcular_operacion(resultado, numero, operador), decimales) + operador = None + + + #elif resultado is None: + #resultado = numero + + + except ValueError: + mostrar_error([3]) + return num1,num2,operador + +def main(): + """ + Función principal de la calculadora. Gestiona la entrada del usuario y coordina las operaciones. + + Note: + El flujo del programa es el siguiente: + + 1. Inicia la calculadora mostrando el resultado almacenado por defecto (0.00). + + 2. El usuario ingresa un comando, que puede ser: + - "lista" para ver todas las operaciones disponibles. + - "ce" para reiniciar el resultado almacenado a 0. + - "decimales " para establecer el número de decimales mostrados en el resultado. + - "calculo" para iniciar una secuencia de cálculo paso a paso. + - Una entrada vacía y pulsa la tecla para salir de la calculadora. + + 3. Según el comando ingresado: + - El programa realiza la operación o ejecuta la acción indicada. + - Al ingresar "calculo": + * El usuario es guiado para introducir números y operadores secuencialmente para realizar operaciones básicas. + * El usuario puede utilizar "resultado" en la secuencia de cálculo para reutilizar el resultado almacenado en la calculadora. + * El cálculo finaliza al pulsar , volviendo y actualizando el resultado almacenado de la calculadora con el cálculo realizado. + * También podemos escribir "cancelar", volviendo sin realizar ningún cambio en el resultado almacenado de la calculadora. + + 4. La calculadora sigue ejecutándose hasta que el usuario confirma la salida al ingresar una entrada vacía y pulsar . + + 5. Finalmente, se limpia la pantalla, el programa se despide con el mensaje "\n\nBye, bye...\n\n" y termina. + """ + # TODO: Corrige los errores y haz que el main funcione correctamente... + + decimales = 2 + resultado = 0 + desea_salir = False + while not desea_salir: + + entrada = pedir_entrada(f"Operación (RES => {resultado:{decimales}f} >> ") + + if entrada == "cancelar": + entrada = pedir_entrada("¿Desea salir de la calculadora? (s/n) ") + if entrada == "s": + desea_salir = True + elif entrada == "n": + desea_salir = False + + + elif entrada == "lista": + print(obtener_operaciones()) + + elif entrada == "ce": + resultado = 0.00 + + elif entrada.startswith("decimales"): + # Extraemos las posiciones decimales y las convertimos a un valor entero + decimales = str(entrada.split()[1]) + decimales = int(decimales) + if decimales is None: + mostrar_error([1]) + print(f"Decimales configurados a {decimales}.") + + elif entrada == "calculo": + realizar_calculo(entrada,decimales) + + else: + mostrar_error([6]) + + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/dam1_calculadora/jluis_calculadora_alumnos.py b/dam1_calculadora/jluis_calculadora_alumnos.py new file mode 100644 index 0000000..cfe6239 --- /dev/null +++ b/dam1_calculadora/jluis_calculadora_alumnos.py @@ -0,0 +1,473 @@ +# TODO: ATENCIÓN!!! +# 1. Importar los paquetes necesarios +# 2. Usar la función mostrar_error para imprimir los errores por consola. +# 3. Los comentarios TODO os ayudan a resolver cada apartado de esta prueba. +# 4. CADA función SOLO puede tener una instrucción return. +# 5. En test_calculadora_alumnos.py crear el test unitario para la función es_resultado_negativo y hacer que todos los test unitarios se cumplan. + +import os +import platform + +# Mensajes de error predefinidos +MENSAJES_ERROR = ( + "Problemas al intentar limpiar la pantalla {error}", + "Error al configurar los decimales. Formato: decimales .", + "Entrada no válida. Ingrese número, operador, 'resultado', 'cancelar' o para finalizar el cálculo.", + "Error: Introduzca un operador antes de otro número.", + "Comando no reconocido. Escriba 'lista' para ver las operaciones disponibles.", + "Error: no es posible la división por 0! Introduzca otro valor diferente a 0...", + "Se produjo un error: {error}" +) + +# Operadores soportados por la calculadora +OPERADORES = ('+', '-', 'x', '*', '/', ':', '**', 'exp') +# TODO: Ayuda: incluye en esta constante los símbolos que reconocerá la aplicación para el cálculo de las operaciones: +# '+' para la suma. +# '-' para la resta. +# 'x' o '*' para la multiplicación. +# '/' o ':' para la división. +# '**' o 'exp' para la potencia. + +COMANDOS = ('', 'ce', 'lista', 'calculo', 'decimales') + + +def limpiar_pantalla(): + """ + Limpia la consola según el sistema operativo. + """ + # TODO: El desarrollo de esta función está incompleto y con errores. + try: + sistema_operativo = platform.system() + + if sistema_operativo == "Linux" or sistema_operativo == "Darwin": + os.system('clear') + else: + os.system('cls') + + except Exception as e: + mostrar_error(0, e) + + + +def pausa(): + """ + Pausa la ejecución del programa hasta que se pulse ENTER... "\nPresione ENTER para continuar..." + """ + # TODO: Desarrollar esta función según su documentación. + enter = input('\nPresione ENTER para continuar...') + + +def mostrar_error(indice_error: int, msj_error = None): + """ + Muestra un mensaje de error en la consola. + + Args: + indice_error (int): Índice del mensaje de error en MENSAJES_ERROR. + msj_error (str, opcional): Texto adicional para personalizar el mensaje de error. + """ + # TODO: + # 1. Corrige el error que existe a la hora de acceder a los mensajes de error que están en la constante + # MENSAJES_ERROR. A los elementos de una tupla se accede igual que a los caracteres de una + # cadena de caracteres. + # 2. Completa el código de esta función para que controle específicamente las excepciones IndexError y + # muestre el mensaje: "\n*ERROR* Mensaje de error no definido.\n" + # 3. También se pide que se controle cualquier otra excepción que se pueda producir y muestre el mensaje: + # "\n*ERROR* Problemas al mostrar error!\n{e}\n" + # 4. En esta función los mensajes de error deben mostrarse con print. + try: + if msj_error != None: + print(f"\n*ERROR* {MENSAJES_ERROR.format(error = msj_error)}\n") + else: + print(f"\n*ERROR* {MENSAJES_ERROR[indice_error]}\n") + except IndexError: + print("\n*ERROR* Mensaje de error no definido.\n") + except Exception as e: + print(f"\n*ERROR* Problemas al mostrar error!\n{e}\n") + + +def sumar(num1: float, num2: float) -> float: + ''' + Esta funcion suma los numeros introducidos + + Args: + num1 (float): Primer numero + num2 (float): Segundo numero + + Returns: + float: Resultado de la resta + ''' + return num1 + num2 + + +def restar(num1: float, num2: float) -> float: + ''' + Esta funcion resta los numeros introducidos + + Args: + num1 (float): Primer numero + num2 (float): Segundo numero + + Returns: + float: Resultado de la resta + ''' + return num1 - num2 + + +def es_resultado_negativo(num1: float, num2: float) -> bool: + """Determina si el resultado de una operación entre num1 y num2 debe ser negativo.""" + # TODO: Realizar el desarrollo completo de esta función teniendo en cuenta la documentación y completándola también. + # Debe pasar las pruebas unitarias. + # Se trata de una función que os puede venir bien para utilizarla tanto en la función multiplicar, como en dividir. + # Ya que va a determinar si la multiplicación o división entre dos números debería ser de signo negativo o no. + if num1 < 0 and num2 < 0: + negativo = False + elif num1 < 0 or num2 < 0: + negativo = True + else: + negativo = False + + return negativo + +def multiplicar(num1: float, num2: float) -> int: + """ + Realiza la multiplicación ENTERA de dos números usando solo sumas y restas. + + Args: + num1 (float): Primer número. + num2 (float): Segundo número. + + Returns: + int: Resultado de la multiplicación. + + Note: + Debe redondear los números recibidos a enteros para trabajar. + """ + # TODO: + # 1. Realizar el desarrollo completo de esta función teniendo en cuenta la documentación. + # 2. Esta función debe pasar las pruebas unitarias. + # 3. No podéis usar el operador de multiplicación de Python para realizar el desarrollo de la misma, + # es decir, que debéis realizar la multiplicación con SUMAS y/o RESTAS... + # 4. Aunque se reciben números de tipo float, debéis redondearlos como números enteros para + # simplificar esta función, es decir, la operación 4.98 * 3.33, deberá convertirse en 5 * 3. + # 5. Tened en cuenta que podéis recibir números negativos, es decir, la operación -5 * -5 = 25 + # 6. OBLIGATORIO usar un bucle for. + # 7. Incluir algún comentario para mejorar la claridad y permitir que otros comprendan el propósito y funcionamiento del código. + negativo = es_resultado_negativo(num1, num2) + resultado = 0 + + # Ponemos los numeros en valor absoluto para operar con ellos + num1 = round(abs(num1)) + num2 = round(abs(num2)) + + # A Diego le apetece que lo hagamos con un for, asi que para contentar a Diego hacemos un bucle for + # que sume uno de los numeros, en este caso el num1, tantas veces como el num2. Debido a la propiedad + # conmutativa de la multiplicacion, da igual cual sumemos. + for i in range (num2): + resultado += num1 + + # Invertimos el resultado + resultado_negativo = resultado - resultado - resultado + + return resultado_negativo if negativo else resultado + + + +def dividir(num1: float, num2: float): + """ + Realiza la división ENTERA de dos números usando solo sumas y restas. + + Args: + num1 (float): Dividendo. + num2 (float): Divisor. + + Returns: + int: Resultado de la división. + + Raises: + ZeroDivisionError: Si el divisor es cero. + + Note: + Debe redondear los números recibidos a enteros para trabajar. + """ + # TODO: + # 1. Realizar el desarrollo completo de esta función teniendo en cuenta la documentación. + # 2. Esta función debe pasar las pruebas unitarias. + # 3. No podéis usar el operador de división de Python para realizar el desarrollo de la misma, + # es decir, que debéis realizar la división con SUMAS y/o RESTAS... + # 4. Aunque se reciben números de tipo float, debéis redondearlos como números enteros para + # simplificar esta función, es decir, la operación 4.98 / 3.33, deberá convertirse en 5 / 3. + # 5. Tened en cuenta que podéis recibir números negativos, es decir, la operación -5 / -5 = 1 + # 6. Incluir algún comentario para mejorar la claridad y permitir que otros comprendan el propósito y funcionamiento del código. + resultado = num1 + contador = 0 + negativo = es_resultado_negativo(num1, num2) + + # Ponemos los numeros en valor absoluto para operar con ellos + num1 = round(abs(num1)) + num2 = round(abs(num2)) + + # A Diego le apetece que lo hagamos con un while, asi que para contentar a Diego hacemos un bucle while + # que cuente el numero de veces que le podemos restar el dividendo al divisor. + while resultado > 0: + resultado -= num2 + contador += 1 + + # Invertimos el resultado + contador_negativo = contador - contador - contador + + return contador_negativo if negativo else contador + + +def potencia(num1: float, num2: float) -> int: + ''' + Esta funcion calcula la potencia de un numero + + Args: + num1 (float): Numero a elevar + num2 (float): Exponente + + Returns: + int: Resultado + ''' + # TODO: + # 1. Realizar el desarrollo completo de esta función y su documentación. + # 2. Esta función debe pasar las pruebas unitarias. + # 3. PREMISAS a tener en cuenta: + # - Cualquier número elevado a 0 da como resultado 1. + # - Para simplificar esta función, vamos a suponer que un número elevado a un + # exponente negativo siempre dará 0 (aunque en realidad no es así matemáticamente) + # 4. Utiliza la función de multiplicar para realizar los cálculos que te + # harán falta en esta función (RECUERDA que no puedes usar directamente los operadores + # de Python para la multiplicación y división). + # 5. Incluir algún comentario para mejorar la claridad y permitir que otros comprendan el propósito y funcionamiento del código. + negativo = es_resultado_negativo(num1, num2) + num1 = round(abs(num1)) + num2 = round(abs(num2)) + resultado = 1 + + if num2 != 0: + for i in range (num2): + resultado = multiplicar(resultado, num1) + elif num2 < 0: + resultado = 0 + else: + resultado = 1 + + resultado_negativo = resultado - resultado - resultado + + modulo = num2 % 2 + + return resultado_negativo if negativo and modulo != 0 else resultado + +def pedir_entrada(msj: str) -> str: + """ + Pide al usuario una entrada, elimina espacios por delante y por detrás y la convierte a minúsculas. + + Args: + msj (str): Mensaje para solicitar la entrada. + + Returns: + str: Entrada del usuario. + """ + # TODO: El desarrollo de esta función está incompleto, debéis terminarla teniendo en cuenta la documentación + return input(msj).lower().strip() + + +def calcular_operacion(num1: float, num2: float, operador: str) -> float: + """ + Realiza la operación especificada entre num1 y num2 dependiendo del valor del operador. + + Args: + num1 (float): Primer número. + num2 (float): Segundo número. + operador (str): Operador de la operación. + + Returns: + float: Resultado de la operación. + + Raises: + ZeroDivisionError: Si el divisor es cero. + """ + # TODO: El desarrollo de esta función está incompleto... completadla teniendo en cuenta la documentación + # y que debe realizar las llamadas adecuadas a las funciones ya creadas para realizar los distintos + # cálculos... sumar, restar, multiplicar, dividir y potencia. + # IMPORTANTE: Si hacemos caso a la documentación de esta función, NO debéis capturar la excepción + # ZeroDivisionError aquí, sino que hay que dejadla que se propague a la función llamante, realizar_calculo(), + # dónde se os indica cómo realizar la gestión de estos errores. + if operador == OPERADORES[0]: + resultado = sumar(num1, num2) + elif operador == OPERADORES[1]: + resultado = restar(num1, num2) + elif operador == OPERADORES[2] or operador == OPERADORES[3]: + resultado = multiplicar(num1, num2) + elif operador == OPERADORES[4] or operador == OPERADORES[5]: + resultado = dividir(num1, num2) + elif operador == OPERADORES[6] or operador == OPERADORES[7]: + resultado = potencia(num1, num2) + + return resultado + + + +def obtener_operaciones() -> str: + """ + Devuelve una cadena con la lista de operaciones disponibles en la calculadora. + + Returns: + (str): cadena de caracteres con la información de las operaciones disponibles. + """ + # TODO: El desarrollo de esta función está incompleto... ver documentación para solucionarla correctamente. + return 'Operaciones disponibles:\nce => Reiniciar resultado a 0\ndecimales => Establecer decimales en resultado\ncadena vacía + => Pregunta si desea salir\ncalculo => Iniciar cálculo secuencial\n + => Suma\n - => Resta\n x o * => Multiplicación\n / o : => División\n ** o exp => Potencia\n cancelar => volver sin actualizar resultado de la calculadora\n cadena vacía + => volver actualizando resultado de la calculadora' + + +def realizar_calculo(decimales: int, resultado_almacenado: float) -> float: + """ + Realiza una secuencia de cálculos solicitando números y operadores al usuario. + + Args: + decimales (int): Número de decimales para el resultado. + resultado_almacenado (float): Valor almacenado en la calculadora. + + Returns: + float: Resultado final del cálculo o None si se cancela. + + Note: + * Dentro de esta función el usuario puede realizar cálculos secuenciales, es decir, + comenzará introduciendo un número, después un operador, y otro número... a partir + de aquí sobre el resultado acumulado, introducirá operador y número para seguir + realizando cálculos (ver ejemplos en README.md de la tarea en el repositorio de GitHub). + * El usuario es guiado para introducir números y operadores secuencialmente + para realizar operaciones básicas. + * El usuario puede utilizar "resultado" en la secuencia de cálculo para reutilizar el + resultado almacenado en la calculadora. + * El cálculo finaliza al pulsar , volviendo y actualizando el resultado almacenado + de la calculadora con el cálculo realizado. + * También podemos escribir "cancelar", volviendo sin realizar ningún cambio en el + resultado almacenado de la calculadora. + """ + # TODO: El desarrollo de esta función está incompleto... ver documentación para solucionarla correctamente. + + operador = None + resultado = None + realizando_calculos = True + + print("\n## Ingrese número, operador, 'resultado', 'cancelar' o para finalizar el cálculo ##\n") + + while realizando_calculos: + if resultado == None: + entrada = pedir_entrada(f"\t (Cálculo = 0.0) >> ") + else: + entrada = pedir_entrada(f"\t (Cálculo = {round(float(resultado), decimales)}) >> ") + + if entrada == "cancelar": + resultado = None + + elif entrada == "": + return resultado + + elif entrada in OPERADORES: + operador = entrada + + else: + # TODO: este código funciona cuando solucionéis que reconozca las variables resultado_almacenado y decimales. + # Pero no gestiona los posibles tipos de Excepciones que se pueden producir: + # - ValueError que debe mostrar el error que está en la posición 2 de MENSAJES_ERROR. + # - ZeroDivisionError que debe mostrar el error que está en la posición 5 de MENSAJES_ERROR. + # - Exception que debe mostrar el error que está en la posición 6 de MENSAJES_ERROR. + if entrada == "resultado": + resultado_almacenado = resultado + resultado = None + entrada = resultado_almacenado + + try: + numero = float(entrada) + + if operador is not None: + if resultado is None: + resultado = 0 + resultado = round(calcular_operacion(resultado, numero, operador), decimales) + operador = None + + elif resultado is None: + resultado = numero + + else: + mostrar_error(3) + + except ValueError: + mostrar_error(2) + except ZeroDivisionError: + mostrar_error(5) + except Exception: + mostrar_error(6) + + +def main(): + """ + Función principal de la calculadora. Gestiona la entrada del usuario y coordina las operaciones. + + Note: + El flujo del programa es el siguiente: + + 1. Inicia la calculadora mostrando el resultado almacenado por defecto (0.00). + + 2. El usuario ingresa un comando, que puede ser: + - "lista" para ver todas las operaciones disponibles. + - "ce" para reiniciar el resultado almacenado a 0. + - "decimales " para establecer el número de decimales mostrados en el resultado. + - "calculo" para iniciar una secuencia de cálculo paso a paso. + - Una entrada vacía y pulsa la tecla para salir de la calculadora. + + 3. Según el comando ingresado: + - El programa realiza la operación o ejecuta la acción indicada. + - Al ingresar "calculo": + * El usuario es guiado para introducir números y operadores secuencialmente para realizar operaciones básicas. + * El usuario puede utilizar "resultado" en la secuencia de cálculo para reutilizar el resultado almacenado en la calculadora. + * El cálculo finaliza al pulsar , volviendo y actualizando el resultado almacenado de la calculadora con el cálculo realizado. + * También podemos escribir "cancelar", volviendo sin realizar ningún cambio en el resultado almacenado de la calculadora. + + 4. La calculadora sigue ejecutándose hasta que el usuario confirma la salida al ingresar una entrada vacía y pulsar . + + 5. Finalmente, se limpia la pantalla, el programa se despide con el mensaje "\n\nBye, bye...\n\n" y termina. + """ + # TODO: Corrige los errores y haz que el main funcione correctamente... + + decimales = 2 + resultado = 0.0 + desea_salir = False + + while not desea_salir: + + if resultado == None: + entrada = pedir_entrada(f"\t (Cálculo = 0.0) >> ") + else: + entrada = pedir_entrada(f"\t (Cálculo = {round(float(resultado), decimales)}) >> ") + + if entrada == "": + salir = pedir_entrada("¿Desea salir de la calculadora? (s/n) ") + if salir == 's': + desea_salir = True + elif entrada == "lista": + lista = obtener_operaciones() + print(lista) + pausa() + elif entrada == "ce": + resultado = 0.0 + elif entrada.startswith("decimales"): + # Extraemos las posiciones decimales y las convertimos a un valor entero + decimales = str(entrada.split()[1]) + decimales = int(decimales) + print(f"Decimales configurados a {decimales}.") + pausa() + elif entrada == "calculo": + resultado = realizar_calculo(decimales, resultado) + else: + mostrar_error(4) + pausa() + + limpiar_pantalla() + print("\n\nBye, bye...\n\n") + + +if __name__ == '__main__': + main() \ No newline at end of file diff --git a/dam1_calculadora/jluis_test_calculadora.py b/dam1_calculadora/jluis_test_calculadora.py new file mode 100644 index 0000000..fa0b3b4 --- /dev/null +++ b/dam1_calculadora/jluis_test_calculadora.py @@ -0,0 +1,70 @@ + +import pytest + +from src.calculadora_alumnos import es_resultado_negativo, multiplicar, dividir, potencia + + +# TODO: Crear el test unitario para la función es_resultado_negativo. Verifica lo siguiente: +# Casos donde el resultado debe ser negativo (2 casos) +# Casos donde el resultado debe ser positivo (2 casos) +def test_es_resultado_negativo(): + assert es_resultado_negativo(-1, 1) == True + assert es_resultado_negativo(-2, 2) == True + assert es_resultado_negativo(1, 1) == False + assert es_resultado_negativo(-1, -1) == False + + +def test_multiplicar(): + # Multiplicación con números positivos + assert multiplicar(3.421, 3.922) == 12 + assert multiplicar(7.11, 2.1) == 14 + + # Multiplicación con un número negativo + assert multiplicar(-3.477, 4.1) == -12 + assert multiplicar(5, -2) == -10 + + # Multiplicación de dos números negativos + assert multiplicar(-5, -3) == 15 + + # Multiplicación con cero + assert multiplicar(0, 5) == 0 + assert multiplicar(3, 0) == 0 + +def test_dividir(): + # División con números positivos + assert dividir(9.33, 3.122) == 3 + assert dividir(14.757, 4.968) == 3 + + # División con un número negativo + assert dividir(-12, 3) == -4 + assert dividir(14.223, -2) == -7 + + # División de dos números negativos + assert dividir(-15.899, -4.499) == 4 + + # División con redondeo a entero + assert dividir(10, 3.101) == 3 # 10 // 3 = 3 (redondeo esperado) + + # División por cero + with pytest.raises(ZeroDivisionError): + dividir(5, 0) + +@pytest.mark.parametrize( + "base, exponente, expected", + [ + (2, 3, 8), # 2^3 = 8 + (5, 0, 1), # 5^0 = 1 + (-2, 3, -8), # -2^3 = -8 (base negativa con exponente impar) + (-2, 4, 16), # -2^4 = 16 (base negativa con exponente par) + (10, 1, 10), # 10^1 = 10 + (3, -2, 0), # 3^-2 = 0 (se devuelve 0 para exponentes negativos) + (0, 5, 0), # 0^5 = 0 + (0, 0, 1), # 0^0 = 1 (por convención en muchas calculadoras) + (5, 2, 25), # 5^2 = 25 + ] +) +def test_potencia(base, exponente, expected): + """ + Prueba para la función potencia. + """ + assert potencia(base, exponente) == expected diff --git a/dam1_calculadora/juanma_calculadora_alumnos.py b/dam1_calculadora/juanma_calculadora_alumnos.py new file mode 100644 index 0000000..8b22689 --- /dev/null +++ b/dam1_calculadora/juanma_calculadora_alumnos.py @@ -0,0 +1,461 @@ +# TODO: ATENCIÓN!!! +# 1. Importar los paquetes necesarios +# 2. Usar la función mostrar_error para imprimir los errores por consola. +# 3. Los comentarios TODO os ayudan a resolver cada apartado de esta prueba. +# 4. CADA función SOLO puede tener una instrucción return. +# 5. En test_calculadora_alumnos.py crear el test unitario para la función es_resultado_negativo y hacer que todos los test unitarios se cumplan. + + + +# Mensajes de error predefinidos +MENSAJES_ERROR = ( + "Problemas al intentar limpiar la pantalla {error}", + "Error al configurar los decimales. Formato: decimales .", + "Entrada no válida. Ingrese número, operador, 'resultado', 'cancelar' o para finalizar el cálculo.", + "Error: Introduzca un operador antes de otro número.", + "Comando no reconocido. Escriba 'lista' para ver las operaciones disponibles.", + "Error: no es posible la división por 0! Introduzca otro valor diferente a 0...", + "Se produjo un error: {error}" +) + +# Operadores soportados por la calculadora +OPERADORES = ["+", "-", "x", "*", "/", ":", "**", "exp"] +# TODO: Ayuda: incluye en esta constante los símbolos que reconocerá la aplicación para el cálculo de las operaciones: +# '+' para la suma. +# '-' para la resta. +# 'x' o '*' para la multiplicación. +# '/' o ':' para la división. +# '**' o 'exp' para la potencia. + + + +def limpiar_pantalla(): + """ + Limpia la consola según el sistema operativo. + """ + # TODO: El desarrollo de esta función está incompleto y con errores. + try: + os.system("clear" if os.name == "posix" else "cls") + # Otra forma de expresar la misma instrucción sería la siguiente: + # if os.name = posix: + # os.system(clear) + # else: + # os.system(cls) + except Exception as e: + mostrar_error(f"Problemas al intentar limpiar la pantalla {e}") + + + +def pausa(): + """ + Pausa la ejecución del programa hasta que se pulse ENTER... "\nPresione ENTER para continuar..." + """ + # TODO: Desarrollar esta función según su documentación. + input("\nPresione ENTER para continuar...") + + + + +def mostrar_error(indice_error: int, msj_error = None): + """ + Muestra un mensaje de error en la consola. + + Args: + indice_error (int): Índice del mensaje de error en MENSAJES_ERROR. + msj_error (str, opcional): Texto adicional para personalizar el mensaje de error. + """ + # TODO: + # 1. Corrige el error que existe a la hora de acceder a los mensajes de error que están en la constante + # MENSAJES_ERROR. A los elementos de una tupla se accede igual que a los caracteres de una + # cadena de caracteres. + # 2. Completa el código de esta función para que controle específicamente las excepciones IndexError y + # muestre el mensaje: "\n*ERROR* Mensaje de error no definido.\n" + # 3. También se pide que se controle cualquier otra excepción que se pueda producir y muestre el mensaje: + # "\n*ERROR* Problemas al mostrar error!\n{e}\n" + # 4. En esta función los mensajes de error deben mostrarse con print. + if msj_error != None: + print(f"\n*ERROR* {MENSAJES_ERROR.format(error = msj_error)}\n") + else: + print(f"\n*ERROR* {MENSAJES_ERROR}\n") + + +def sumar(num1, num2): + # TODO: Realizar el desarrollo completo, incluida la documentación... recibe 2 números float y retorna la suma de ambos. + suma = num1 + num2 + return suma + + + +def restar(num1, num2): + # TODO: Realizar el desarrollo completo, incluida la documentación... recibe 2 números float y retorna la resta de ambos. + resta = num1 - num2 + return resta + + +def es_resultado_negativo(num1: float, num2: float, resultado: int) -> bool: + """Determina si el resultado de una operación entre num1 y num2 debe ser negativo.""" + # TODO: Realizar el desarrollo completo de esta función teniendo en cuenta la documentación y completándola también. + # Debe pasar las pruebas unitarias. + # Se trata de una función que os puede venir bien para utilizarla tanto en la función multiplicar, como en dividir. + # Ya que va a determinar si la multiplicación o división entre dos números debería ser de signo negativo o no. + # Si uno de los números es negativo, el resultado lo será también, pero si lo son ambos, el resultado será positivo. + if num1 == f"- {num1}" or num2 == f"- {num2}": + resultado == f"- {resultado}" + elif num1 == f"- {num1}" and num2 == f"- {num2}": + resultado == f"{resultado}" + elif num1 == f"{num1}" and num2 == f"{num2}": + resultado == f"{resultado}" + return resultado + + +def multiplicar(num1, num2): + """ + Realiza la multiplicación ENTERA de dos números usando solo sumas y restas. + + Args: + num1 (float): Primer número. + num2 (float): Segundo número. + + Returns: + int: Resultado de la multiplicación. + + Note: + Debe redondear los números recibidos a enteros para trabajar. + """ + # TODO: + # 1. Realizar el desarrollo completo de esta función teniendo en cuenta la documentación. + # 2. Esta función debe pasar las pruebas unitarias. + # 3. No podéis usar el operador de multiplicación de Python para realizar el desarrollo de la misma, + # es decir, que debéis realizar la multiplicación con SUMAS y/o RESTAS... + # 4. Aunque se reciben números de tipo float, debéis redondearlos como números enteros para + # simplificar esta función, es decir, la operación 4.98 * 3.33, deberá convertirse en 5 * 3. + # 5. Tened en cuenta que podéis recibir números negativos, es decir, la operación -5 * -5 = 25 + # 6. OBLIGATORIO usar un bucle for. + # 7. Incluir algún comentario para mejorar la claridad y permitir que otros comprendan el propósito y funcionamiento del código. + num1 = abs(num1) + num2 = abs(num2) + + resultado = 0 + + if num1 == 0 or num2 == 0: + return 0 + + for resultado in range(num2): + resultado += num1 + + es_resultado_negativo() + + return resultado + + + + + +def dividir(num1, num2): + """ + Realiza la división ENTERA de dos números usando solo sumas y restas. + + Args: + num1 (float): Dividendo. + num2 (float): Divisor. + + Returns: + int: Resultado de la división. + + Raises: + ZeroDivisionError: Si el divisor es cero. + + Note: + Debe redondear los números recibidos a enteros para trabajar. + """ + # TODO: + # 1. Realizar el desarrollo completo de esta función teniendo en cuenta la documentación. + # 2. Esta función debe pasar las pruebas unitarias. + # 3. No podéis usar el operador de división de Python para realizar el desarrollo de la misma, + # es decir, que debéis realizar la división con SUMAS y/o RESTAS... + # 4. Aunque se reciben números de tipo float, debéis redondearlos como números enteros para + # simplificar esta función, es decir, la operación 4.98 / 3.33, deberá convertirse en 5 / 3. + # 5. Tened en cuenta que podéis recibir números negativos, es decir, la operación -5 / -5 = 1 + # 6. Incluir algún comentario para mejorar la claridad y permitir que otros comprendan el propósito y funcionamiento del código. + num1 = abs(num1) + num2 = abs(num2) + + resultado = 0 + + if num2 == 0: + return 0 + + try: + if num1 != 0: + return True + except ValueError: + input("La operación introducida es una indeterminación, introduce otra: ") + + while num1 > num2: + resultado -= num2 + + es_resultado_negativo() + + for resultado in range(num2): + + return resultado + + +def potencia(num1, num2): + # TODO: + # 1. Realizar el desarrollo completo de esta función y su documentación. + # 2. Esta función debe pasar las pruebas unitarias. + # 3. PREMISAS a tener en cuenta: + # - Cualquier número elevado a 0 da como resultado 1. + # - Para simplificar esta función, vamos a suponer que un número elevado a un + # exponente negativo siempre dará 0 (aunque en realidad no es así matemáticamente) + # 4. Utiliza la función de multiplicar para realizar los cálculos que te + # harán falta en esta función (RECUERDA que no puedes usar directamente los operadores + # de Python para la multiplicación y división). + # 5. Incluir algún comentario para mejorar la claridad y permitir que otros comprendan el propósito y funcionamiento del código. + num1 = abs(num1) + num2 = abs(num2) + + resultado = 0 + + if num2 == 0: + return 1 + elif num2 == f"- {num2}": + return 0 + + + + return resultado + + + + +def pedir_entrada(entrada: str) -> str: + """ + Pide al usuario una entrada, elimina espacios por delante y por detrás y la convierte a minúsculas. + + Args: + msj (str): Mensaje para solicitar la entrada. + + Returns: + str: Entrada del usuario. + """ + # TODO: El desarrollo de esta función está incompleto, debéis terminarla teniendo en cuenta la documentación + entrada = "Introduce una entrada: ".strip().lower() + return input(entrada) + + +def calcular_operacion(num1: float, num2: float, operador: str) -> float: + """ + Realiza la operación especificada entre num1 y num2 dependiendo del valor del operador. + + Args: + num1 (float): Primer número. + num2 (float): Segundo número. + operador (str): Operador de la operación. + + Returns: + float: Resultado de la operación. + + Raises: + ZeroDivisionError: Si el divisor es cero. + """ + # TODO: El desarrollo de esta función está incompleto... completadla teniendo en cuenta la documentación + # y que debe realizar las llamadas adecuadas a las funciones ya creadas para realizar los distintos + # cálculos... sumar, restar, multiplicar, dividir y potencia. + # IMPORTANTE: Si hacemos caso a la documentación de esta función, NO debéis capturar la excepción + # ZeroDivisionError aquí, sino que hay que dejadla que se propague a la función llamante, realizar_calculo(), + # dónde se os indica cómo realizar la gestión de estos errores. + resultado = f"{num1} {operador} {num2}" + + return resultado + + + +def obtener_operaciones() -> str: + """ + Devuelve una cadena con la lista de operaciones disponibles en la calculadora. + + Returns: + (str): cadena de caracteres con la información de las operaciones disponibles. + """ + # TODO: El desarrollo de esta función está incompleto... ver documentación para solucionarla correctamente. + """ + Operaciones disponibles: + ce => Reiniciar resultado a 0 + decimales => Establecer decimales en resultado + cadena vacía + => Pregunta si desea salir + calculo => Iniciar cálculo secuencial + + => Suma + - => Resta + x o * => Multiplicación + / o : => División + ** o exp => Potencia + cancelar => vovler sin actualizar resultado de la calculadora + cadena vacía + => volver actualizando resultado de la calculadora + """ + operador = input("Introduce el tipo de operación que quiere realizar: ") + + try: + if operador == OPERADORES: + return True + except ValueError: + input("Operación inválida, introduzca una de nuevo: ") + + if operador == "+": + sumar() + elif operador == "-": + restar() + elif operador == "x" or operador == "*": + multiplicar() + elif operador == "**" or operador == "exp": + potencia() + else: + dividir() + + return operador + + + +def realizar_calculo(): + """ + Realiza una secuencia de cálculos solicitando números y operadores al usuario. + + Args: + decimales (int): Número de decimales para el resultado. + resultado_almacenado (float): Valor almacenado en la calculadora. + + Returns: + float: Resultado final del cálculo o None si se cancela. + + Note: + * Dentro de esta función el usuario puede realizar cálculos secuenciales, es decir, + comenzará introduciendo un número, después un operador, y otro número... a partir + de aquí sobre el resultado acumulado, introducirá operador y número para seguir + realizando cálculos (ver ejemplos en README.md de la tarea en el repositorio de GitHub). + * El usuario es guiado para introducir números y operadores secuencialmente + para realizar operaciones básicas. + * El usuario puede utilizar "resultado" en la secuencia de cálculo para reutilizar el + resultado almacenado en la calculadora. + * El cálculo finaliza al pulsar , volviendo y actualizando el resultado almacenado + de la calculadora con el cálculo realizado. + * También podemos escribir "cancelar", volviendo sin realizar ningún cambio en el + resultado almacenado de la calculadora. + """ + # TODO: El desarrollo de esta función está incompleto... ver documentación para solucionarla correctamente. + + operador = None + resultado = None + realizando_calculos = True + + print("\n## Ingrese número, operador, 'resultado', 'cancelar' o para finalizar el cálculo ##\n") + + while realizando_calculos: + entrada = pedir_entrada(f"\t (Cálculo = resultado) >> ") + + if entrada == "cancelar": + + + elif entrada == "": + + + elif entrada in OPERADORES: + operador = "" + + else: + # TODO: este código funciona cuando solucionéis que reconozca las variables resultado_almacenado y decimales. + # Pero no gestiona los posibles tipos de Excepciones que se pueden producir: + # - ValueError que debe mostrar el error que está en la posición 2 de MENSAJES_ERROR. + # - ZeroDivisionError que debe mostrar el error que está en la posición 5 de MENSAJES_ERROR. + # - Exception que debe mostrar el error que está en la posición 6 de MENSAJES_ERROR. + if entrada == "resultado": + entrada = resultado_almacenado + + try: + numero = float(entrada) + + if operador is not None: + if resultado is None: + resultado = 0 + resultado = round(calcular_operacion(resultado, numero, operador), decimales) + operador = None + + elif resultado is None: + resultado = numero + + else: + mostrar_error(3) + except ValueError: + + +def main(): + """ + Función principal de la calculadora. Gestiona la entrada del usuario y coordina las operaciones. + + Note: + El flujo del programa es el siguiente: + + 1. Inicia la calculadora mostrando el resultado almacenado por defecto (0.00). + + 2. El usuario ingresa un comando, que puede ser: + - "lista" para ver todas las operaciones disponibles. + - "ce" para reiniciar el resultado almacenado a 0. + - "decimales " para establecer el número de decimales mostrados en el resultado. + - "calculo" para iniciar una secuencia de cálculo paso a paso. + - Una entrada vacía y pulsa la tecla para salir de la calculadora. + + 3. Según el comando ingresado: + - El programa realiza la operación o ejecuta la acción indicada. + - Al ingresar "calculo": + * El usuario es guiado para introducir números y operadores secuencialmente para realizar operaciones básicas. + * El usuario puede utilizar "resultado" en la secuencia de cálculo para reutilizar el resultado almacenado en la calculadora. + * El cálculo finaliza al pulsar , volviendo y actualizando el resultado almacenado de la calculadora con el cálculo realizado. + * También podemos escribir "cancelar", volviendo sin realizar ningún cambio en el resultado almacenado de la calculadora. + + 4. La calculadora sigue ejecutándose hasta que el usuario confirma la salida al ingresar una entrada vacía y pulsar . + + 5. Finalmente, se limpia la pantalla, el programa se despide con el mensaje "\n\nBye, bye...\n\n" y termina. + """ + # TODO: Corrige los errores y haz que el main funcione correctamente... + + decimales = 2 + resultado = 0.0 + + limpiar_pantalla() + pausa() + num1 = float(input("Introduce el primer número: ")) + num2 = float(input("Introduce el segundo número: ")) + suma = print(sumar(num1, num2)) + resta = print(restar(num1, num2)) + entrada = pedir_entrada() + print(realizar_calculo()) + + while not desea_salir: + + pedir_entrada(f"Operación (RES => resultado) >> ") + + if entrada == "": + pedir_entrada("¿Desea salir de la calculadora? (s/n) ") + + elif entrada == "lista": + obtener_operaciones + + elif entrada == "ce": + + + elif entrada.startswith("decimales"): + # Extraemos las posiciones decimales y las convertimos a un valor entero + decimales = str(entrada.split()[1]) + print(f"Decimales configurados a {decimales}.") + + elif entrada == "calculo": + realizar_calculo(decimales, resultado) + + else: + mostrar_error + + + + +if __name__ == "__main__": + main() diff --git a/dam1_calculadora/luismi_calculadora_alumnos.py b/dam1_calculadora/luismi_calculadora_alumnos.py new file mode 100644 index 0000000..26914fb --- /dev/null +++ b/dam1_calculadora/luismi_calculadora_alumnos.py @@ -0,0 +1,500 @@ +# TODO: ATENCIÓN!!! +# 1. Importar los paquetes necesarios +# 2. Usar la función mostrar_error para imprimir los errores por consola. +# 3. Los comentarios TODO os ayudan a resolver cada apartado de esta prueba. +# 4. CADA función SOLO puede tener una instrucción return. +# 5. En test_calculadora_alumnos.py crear el test unitario para la función es_resultado_negativo y hacer que todos los test unitarios se cumplan. + + + +# Mensajes de error predefinidos +import os + + +MENSAJES_ERROR = ( + "Problemas al intentar limpiar la pantalla {error}", + "Error al configurar los decimales. Formato: decimales .", + "Entrada no válida. Ingrese número, operador, 'resultado', 'cancelar' o para finalizar el cálculo.", + "Error: Introduzca un operador antes de otro número.", + "Comando no reconocido. Escriba 'lista' para ver las operaciones disponibles.", + "Error: no es posible la división por 0! Introduzca otro valor diferente a 0...", + "Se produjo un error: {error}" +) + +# Operadores soportados por la calculadora +OPERADORES = "+","-","x","*","/",":","**","exp" +# TODO: Ayuda: incluye en esta constante los símbolos que reconocerá la aplicación para el cálculo de las operaciones: +# '+' para la suma. +# '-' para la resta. +# 'x' o '*' para la multiplicación. +# '/' o ':' para la división. +# '**' o 'exp' para la potencia. + + + +def limpiar_pantalla(): + """ + Limpia la consola según el sistema operativo. + """ + # TODO: El desarrollo de esta función está incompleto y con errores. + try: + if os.name== "posix": + os.system("clear") + else: + os.system("cls") + except Exception as e: + mostrar_error(0,e) + # Otra forma de expresar la misma instrucción sería la siguiente: + # if os.name = posix: + # os.system(clear) + # else: + # os.system(cls) + + + +def pausa(): + """ + Pausa la ejecución del programa hasta que se pulse ENTER... "\nPresione ENTER para continuar..." + """ + # TODO: Desarrollar esta función según su documentación. + input("\nPresione ENTER para continuar...") + +def mostrar_error(indice_error: int, msj_error = None): + """ + Muestra un mensaje de error en la consola. + + Args: + indice_error (int): Índice del mensaje de error en MENSAJES_ERROR. + msj_error (str, opcional): Texto adicional para personalizar el mensaje de error. + """ + # TODO: + # 1. Corrige el error que existe a la hora de acceder a los mensajes de error que están en la constante + # MENSAJES_ERROR. A los elementos de una tupla se accede igual que a los caracteres de una + # cadena de caracteres. + # 2. Completa el código de esta función para que controle específicamente las excepciones IndexError y + # muestre el mensaje: "\n*ERROR* Mensaje de error no definido.\n" + # 3. También se pide que se controle cualquier otra excepción que se pueda producir y muestre el mensaje: + # "\n*ERROR* Problemas al mostrar error!\n{e}\n" + # 4. En esta función los mensajes de error deben mostrarse con print. + if msj_error != None: + try: + print(f"\n*ERROR* {MENSAJES_ERROR[indice_error]}\n") + except IndexError: + print("\n **ERROR** Mensaje de error no definido.\n") + except Exception as e: + print("\n*ERROR*Problemas al mostrar el error!\n{e}\n") + else: + try: + print(f"\n*ERROR* {MENSAJES_ERROR[indice_error]} {msj_error}\n") + except IndexError: + print("\n **ERROR** Mensaje de error no definido.\n") + except Exception as e: + print("\n*ERROR*Problemas al mostrar el error!\n{e}\n") + +def sumar(num1:float,num2:float)->float: + """ + Args: + num1 (float): Primer número introducido + num2 (float): Segundo número introducido + + Returns: + suma: devuelve la suma de ambos números. + """ + suma= num1+num2 + return suma + # TODO: Realizar el desarrollo completo, incluida la documentación... recibe 2 números float y retorna la suma de ambos. + + + +def restar(num1:float,num2:float)->float: + """ + Función que resta dos números. + + Args: + num1(float)-> Primer número introducido + num2(float)-> Segundo número introducido + + Returns: + resta-> Retorna la resta entre ambos números + """ + resta= num1-num2 + return resta + # TODO: Realizar el desarrollo completo, incluida la documentación... recibe 2 números float y retorna la resta de ambos. + + + +def es_resultado_negativo(num1: float, num2: float,operador="") -> bool: + """Determina si el resultado de una operación entre num1 y num2 debe ser negativo. + + Args: + Mirará la división entre dos números o la multiplicación entre dos números + Luego comprobará si es positiva o negativa (division<0 or multiplicacion <0) + + Returns: + True o False + """ + num1= float(num1) + num2=float(num2) + if operador=="/": + division=num1/num2 + if division<0: + return True + else: + return False + elif operador=="*": + multiplicacion= num1 * num2 + if multiplicacion <0: + return True + else: + return False + # TODO: Realizar el desarrollo completo de esta función teniendo en cuenta la documentación y completándola también. + # Debe pasar las pruebas unitarias. + # Se trata de una función que os puede venir bien para utilizarla tanto en la función multiplicar, como en dividir. + # Ya que va a determinar si la multiplicación o división entre dos números debería ser de signo negativo o no. + + + +def multiplicar(num1:float,num2:float)->int: + + """ + Realiza la multiplicación ENTERA de dos números usando solo sumas y restas. + + Args: + num1 (float): Primer número. + num2 (float): Segundo número. + Returns: + int: Resultado de la multiplicación. + + Note: + Debe redondear los números recibidos a enteros para trabajar. + """ + negativo="" + num1= f"{num1:.{0}f}" + num2= f"{num2:.{0}f}" + if es_resultado_negativo(num1,num2,"*")==True: + if str(num1).startswith("-")==1: + num1=num1[1:] + negativo="-" + elif str(num2).startswith("-")==1: + num2=num2[1:] + negativo="-" + multiplicacion=0 + for i in range(1,int(num2)+1): #desde 0 hasta num 2 + multiplicacion+=int(num1) + return f"{negativo}{multiplicacion}" + # TODO: + # 1. Realizar el desarrollo completo de esta función teniendo en cuenta la documentación. + # 2. Esta función debe pasar las pruebas unitarias. + # 3. No podéis usar el operador de multiplicación de Python para realizar el desarrollo de la misma, + # es decir, que debéis realizar la multiplicación con SUMAS y/o RESTAS... + # 4. Aunque se reciben números de tipo float, debéis redondearlos como números enteros para + # simplificar esta función, es decir, la operación 4.98 * 3.33, deberá convertirse en 5 * 3. + # 5. Tened en cuenta que podéis recibir números negativos, es decir, la operación -5 * -5 = 25 + # 6. OBLIGATORIO usar un bucle for. + # 7. Incluir algún comentario para mejorar la claridad y permitir que otros comprendan el propósito y funcionamiento del código. + + + +def dividir(num1:float,num2:float)->int: + """ + Realiza la división ENTERA de dos números usando solo sumas y restas. + + Args: + num1 (float): Dividendo. + num2 (float): Divisor. + + Returns: + int: Resultado de la división. + + Raises: + ZeroDivisionError: Si el divisor es cero. + + Note: + Debe redondear los números recibidos a enteros para trabajar. + """ + num1= f"{num1:.{0}f}" + num1= int(num1) + + num2= f"{num2:.{0}f}" + num2= int(num2) + if num2== 0: + raise ZeroDivisionError("El divisor no puede ser 0") + while num1 >= num2: + num1-=2 + division = num1 + return division + # TODO: + # 1. Realizar el desarrollo completo de esta función teniendo en cuenta la documentación. + # 2. Esta función debe pasar las pruebas unitarias. + # 3. No podéis usar el operador de división de Python para realizar el desarrollo de la misma, + # es decir, que debéis realizar la división con SUMAS y/o RESTAS... + # 4. Aunque se reciben números de tipo float, debéis redondearlos como números enteros para + # simplificar esta función, es decir, la operación 4.98 / 3.33, deberá convertirse en 5 / 3. + # 5. Tened en cuenta que podéis recibir números negativos, es decir, la operación -5 / -5 = 1 + # 6. Incluir algún comentario para mejorar la claridad y permitir que otros comprendan el propósito y funcionamiento del código. + + +def potencia(num1:float,num2:float)->int: + """ + Función que hace la potencia entre dos números. + + Args: + num1(float)-> Primer número introducido + num2(float)-> Segundo número introducido + + Returns: + Potencia-> Devuelve el resultado de la potencia entre ambos números + """ + + # TODO: + # 1. Realizar el desarrollo completo de esta función y su documentación. + # 2. Esta función debe pasar las pruebas unitarias. + # 3. PREMISAS a tener en cuenta: + # - Cualquier número elevado a 0 da como resultado 1. + # - Para simplificar esta función, vamos a suponer que un número elevado a un + # exponente negativo siempre dará 0 (aunque en realidad no es así matemáticamente) + # 4. Utiliza la función de multiplicar para realizar los cálculos que te + # harán falta en esta función (RECUERDA que no puedes usar directamente los operadores + # de Python para la multiplicación y división). + # 5. Incluir algún comentario para mejorar la claridad y permitir que otros comprendan el propósito y funcionamiento del código. + + +def pedir_entrada(msj: str) -> str: + """ + Pide al usuario una entrada, elimina espacios por delante y por detrás y la convierte a minúsculas. + + Args: + msj (str): Mensaje para solicitar la entrada. + + Returns: + str: Entrada del usuario. + """ + # TODO: El desarrollo de esta función está incompleto, debéis terminarla teniendo en cuenta la documentación + valor=input(msj) + return valor.strip().lower() + +def calcular_operacion(num1: float, num2: float, operador: str) -> float: + """ + Realiza la operación especificada entre num1 y num2 dependiendo del valor del operador. + + Args: + num1 (float): Primer número. + num2 (float): Segundo número. + operador (str): Operador de la operación. + + Returns: + float: Resultado de la operación. + + Raises: + ZeroDivisionError: Si el divisor es cero. + """ + # TODO: El desarrollo de esta función está incompleto... completadla teniendo en cuenta la documentación + # y que debe realizar las llamadas adecuadas a las funciones ya creadas para realizar los distintos + # cálculos... sumar, restar, multiplicar, dividir y potencia. + # IMPORTANTE: Si hacemos caso a la documentación de esta función, NO debéis capturar la excepción + # ZeroDivisionError aquí, sino que hay que dejadla que se propague a la función llamante, realizar_calculo(), + # dónde se os indica cómo realizar la gestión de estos errores. + if operador=="+": + resultado =sumar(num1,num2) + + elif operador=="-": + resultado=restar(num1,num2) + + elif operador=="x" or operador=="*": + resultado = multiplicar(num1,num2) + + elif operador== "/" or operador==":": + resultado= dividir(num1,num2) + + elif operador=="**" or operador=="exp": + resultado= potencia(num1,num2) + + return resultado + + + +def obtener_operaciones() -> str: + """ + Devuelve una cadena con la lista de operaciones disponibles en la calculadora. + + Returns: + (str): cadena de caracteres con la información de las operaciones disponibles. + """# TODO: El desarrollo de esta función está incompleto... ver documentación para solucionarla correctamente. + #NOTA: He quitado las operaciones de la documentación y las he metido en prints. + print("Operaciones disponibles:") + print("ce => Reiniciar resultado a 0") + print("decimales => Establecer decimales en resultado") + print("cadena vacía + => Pregunta si desea salir") + print("calculo => Iniciar cálculo secuencial") + print("+ => Suma") + print("- => Resta") + print("x o * => Multiplicación") + print("/ o : => División") + print("** o exp => Potencia") + print("cancelar => vovler sin actualizar resultado de la calculadora") + print("cadena vacía + => volver actualizando resultado de la calculadora") + + + +def realizar_calculo(decimales,resultado)->float: + """ + Realiza una secuencia de cálculos solicitando números y operadores al usuario. + + Args: + decimales (int): Número de decimales para el resultado. + resultado_almacenado (float): Valor almacenado en la calculadora. + + Returns: + float: Resultado final del cálculo o None si se cancela. + + Note: + * Dentro de esta función el usuario puede realizar cálculos secuenciales, es decir, + comenzará introduciendo un número, después un operador, y otro número... a partir + de aquí sobre el resultado acumulado, introducirá operador y número para seguir + realizando cálculos (ver ejemplos en README.md de la tarea en el repositorio de GitHub). + * El usuario es guiado para introducir números y operadores secuencialmente + para realizar operaciones básicas. + * El usuario puede utilizar "resultado" en la secuencia de cálculo para reutilizar el + resultado almacenado en la calculadora. + * El cálculo finaliza al pulsar , volviendo y actualizando el resultado almacenado + de la calculadora con el cálculo realizado. + * También podemos escribir "cancelar", volviendo sin realizar ningún cambio en el + resultado almacenado de la calculadora. + """ + # TODO: El desarrollo de esta función está incompleto... ver documentación para solucionarla correctamente. + resultado_almacenado= float(resultado) + operador = None + resultado = 0 + realizando_calculos = True + print("\n## Ingrese número, operador, 'resultado', 'cancelar' o para finalizar el cálculo ##\n") + + while realizando_calculos: + resultado_almacenado= resultado + entrada = pedir_entrada(f"\t (Cálculo = {resultado_almacenado:.{decimales}f}) >> ") + + if entrada == "cancelar": + print("Volviendo sin realizar ningún cambio en consola") + pausa() + return None + + elif entrada == "": + print("Volviendo y actualizando el resultado almacenado de la calculadora con el cálculo realizado...") + pausa() + return float(resultado) + + elif entrada in OPERADORES: + operador = entrada + + else: + # TODO: este código funciona cuando solucionéis que reconozca las variables resultado_almacenado y decimales. + # Pero no gestiona los posibles tipos de Excepciones que se pueden producir: + # - ValueError que debe mostrar el error que está en la posición 2 de MENSAJES_ERROR. + # - ZeroDivisionError que debe mostrar el error que está en la posición 5 de MENSAJES_ERROR. + # - Exception que debe mostrar el error que está en la posición 6 de MENSAJES_ERROR. + if entrada == "resultado": + entrada = resultado_almacenado + + try: + numero = float(entrada) + + if operador is not None: + if resultado is None: + resultado = 0 + resultado = round(calcular_operacion(resultado, numero, operador), decimales) + operador = None + + elif operador is None: + resultado = numero + + else: + mostrar_error(3) + except ValueError as e: + mostrar_error(2,e) + except ZeroDivisionError as e: + mostrar_error(5,e) + except Exception as e: + mostrar_error(6,e) + + +def main(): + """ + Función principal de la calculadora. Gestiona la entrada del usuario y coordina las operaciones. + + Note: + El flujo del programa es el siguiente: + + 1. Inicia la calculadora mostrando el resultado almacenado por defecto (0.00). + + 2. El usuario ingresa un comando, que puede ser: + - "lista" para ver todas las operaciones disponibles. + - "ce" para reiniciar el resultado almacenado a 0. + - "decimales " para establecer el número de decimales mostrados en el resultado. + - "calculo" para iniciar una secuencia de cálculo paso a paso. + - Una entrada vacía y pulsa la tecla para salir de la calculadora. + + 3. Según el comando ingresado: + - El programa realiza la operación o ejecuta la acción indicada. + - Al ingresar "calculo": + * El usuario es guiado para introducir números y operadores secuencialmente para realizar operaciones básicas. + * El usuario puede utilizar "resultado" en la secuencia de cálculo para reutilizar el resultado almacenado en la calculadora. + * El cálculo finaliza al pulsar , volviendo y actualizando el resultado almacenado de la calculadora con el cálculo realizado. + * También podemos escribir "cancelar", volviendo sin realizar ningún cambio en el resultado almacenado de la calculadora. + + 4. La calculadora sigue ejecutándose hasta que el usuario confirma la salida al ingresar una entrada vacía y pulsar . + + 5. Finalmente, se limpia la pantalla, el programa se despide con el mensaje "\n\nBye, bye...\n\n" y termina. + """ + # TODO: Corrige los errores y haz que el main funcione correctamente... + + decimales = 2 + resultado = 0 + desea_salir=False + while not desea_salir: + limpiar_pantalla() + entrada=pedir_entrada(f"Operación (RES => {resultado:.{decimales}f}) >> ") + + if entrada == "": + salir=pedir_entrada("¿Desea salir de la calculadora? (s/n) ") + if salir== "s": + desea_salir=True + else: + desea_salir=False + + elif entrada == "lista": + obtener_operaciones() + pausa() + desea_salir=False + + elif entrada == "ce": + print("Resultado reiniciado a cero") + resultado=0 + pausa() + desea_salir=False + + elif entrada.startswith("decimales"): + # Extraemos las posiciones decimales y las convertimos a un valor entero + try: + decimales = str(entrada.split(" ")[1]) + decimales= int(decimales) + print(f"Decimales configurados a {decimales}.") + except Exception: + mostrar_error(1) + desea_salir=False + pausa() + desea_salir=False + + elif entrada == "calculo": + calculo=realizar_calculo(decimales, resultado) + if calculo== None: + desea_salir=False + else: + resultado= calculo + else: + mostrar_error("Introduce un comando válido.") + pausa() + desea_salir=False + limpiar_pantalla() + print("\n\nBye, bye...\n\n") + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/dam1_calculadora/luismi_test_calculadora.py b/dam1_calculadora/luismi_test_calculadora.py new file mode 100644 index 0000000..076422d --- /dev/null +++ b/dam1_calculadora/luismi_test_calculadora.py @@ -0,0 +1,71 @@ + +import pytest + +from src.calculadora_alumnos import es_resultado_negativo, multiplicar, dividir, potencia + + +# TODO: Crear el test unitario para la función es_resultado_negativo. Verifica lo siguiente: +# Casos donde el resultado debe ser negativo (2 casos) +# Casos donde el resultado debe ser positivo (2 casos) + + +def test_negativo(): + assert es_resultado_negativo(5,-5,"*")== True + assert es_resultado_negativo(-8,1,"/")==True + assert es_resultado_negativo(5,5,"*")==False + assert es_resultado_negativo(6,6,"/")==False + +def test_multiplicar(): + # Multiplicación con números positivos + assert multiplicar(3.421, 3.922) == 12 + assert multiplicar(7.11, 2.1) == 14 + + # Multiplicación con un número negativo + assert multiplicar(-3.477, 4.1) == -12 + assert multiplicar(5, -2) == -10 + + # Multiplicación de dos números negativos + assert multiplicar(-5, -3) == 15 + + # Multiplicación con cero + assert multiplicar(0, 5) == 0 + assert multiplicar(3, 0) == 0 + +def test_dividir(): + # División con números positivos + assert dividir(9.33, 3.122) == 3 + assert dividir(14.757, 4.968) == 3 + + # División con un número negativo + assert dividir(-12, 3) == -4 + assert dividir(14.223, -2) == -7 + + # División de dos números negativos + assert dividir(-15.899, -4.499) == 4 + + # División con redondeo a entero + assert dividir(10, 3.101) == 3 # 10 // 3 = 3 (redondeo esperado) + + # División por cero + with pytest.raises(ZeroDivisionError): + dividir(5, 0) + +@pytest.mark.parametrize( + "base, exponente, expected", + [ + (2, 3, 8), # 2^3 = 8 + (5, 0, 1), # 5^0 = 1 + (-2, 3, -8), # -2^3 = -8 (base negativa con exponente impar) + (-2, 4, 16), # -2^4 = 16 (base negativa con exponente par) + (10, 1, 10), # 10^1 = 10 + (3, -2, 0), # 3^-2 = 0 (se devuelve 0 para exponentes negativos) + (0, 5, 0), # 0^5 = 0 + (0, 0, 1), # 0^0 = 1 (por convención en muchas calculadoras) + (5, 2, 25), # 5^2 = 25 + ] +) +def test_potencia(base, exponente, expected): + """ + Prueba para la función potencia. + """ + assert potencia(base, exponente) == expected diff --git a/dam1_calculadora/mario_calculadora_alumnos.py b/dam1_calculadora/mario_calculadora_alumnos.py new file mode 100644 index 0000000..06e63b3 --- /dev/null +++ b/dam1_calculadora/mario_calculadora_alumnos.py @@ -0,0 +1,489 @@ +import os +import time + +# TODO: ATENCIÓN!!! +# 1. Importar los paquetes necesarios +# 2. Usar la función mostrar_error para imprimir los errores por consola. +# 3. Los comentarios TODO os ayudan a resolver cada apartado de esta prueba. +# 4. CADA función SOLO puede tener una instrucción return. +# 5. En test_calculadora_alumnos.py crear el test unitario para la función es_resultado_negativo y hacer que todos los test unitarios se cumplan. + + + +# Mensajes de error predefinidos +MENSAJES_ERROR = ( + "Problemas al intentar limpiar la pantalla {error}", + "Error al configurar los decimales. Formato: decimales .", + "Entrada no válida. Ingrese número, operador, 'resultado', 'cancelar' o para finalizar el cálculo.", + "Error: Introduzca un operador antes de otro número.", + "Comando no reconocido. Escriba 'lista' para ver las operaciones disponibles.", + "Error: no es posible la división por 0! Introduzca otro valor diferente a 0...", + "Se produjo un error: {error}" +) + +# Operadores soportados por la calculadora +OPERADORES = ( + "+", "-", "x", "*", "/", ":", "**", "exp" +) +# TODO: Ayuda: incluye en esta constante los símbolos que reconocerá la aplicación para el cálculo de las operaciones: +# '+' para la suma. +# '-' para la resta. +# 'x' o '*' para la multiplicación. +# '/' o ':' para la división. +# '**' o 'exp' para la potencia. + + + +def limpiar_pantalla(): + """ + Limpia la consola según el sistema operativo. + """ + # TODO: El desarrollo de esta función está incompleto y con errores. + try: + os.system("clear" if os.name == "posix" else "cls") + # Otra forma de expresar la misma instrucción sería la siguiente: + # if os.name = posix: + # os.system(clear) + # else: + # os.system(cls) + except Exception as e: + mostrar_error(0, e) + + + +def pausa(): + """ + Pausa la ejecución del programa hasta que se pulse ENTER... "\nPresione ENTER para continuar..." + """ + # TODO: Desarrollar esta función según su documentación. + input("\nPresione ENTER para continuar...") + + + +def mostrar_error(indice_error: int, msj_error = None): + """ + Muestra un mensaje de error en la consola. + + Args: + indice_error (int): Índice del mensaje de error en MENSAJES_ERROR. + msj_error (str, opcional): Texto adicional para personalizar el mensaje de error. + """ + # TODO: + # 1. Corrige el error que existe a la hora de acceder a los mensajes de error que están en la constante + # MENSAJES_ERROR. A los elementos de una tupla se accede igual que a los caracteres de una + # cadena de caracteres. + # 2. Completa el código de esta función para que controle específicamente las excepciones IndexError y + # muestre el mensaje: "\n*ERROR* Mensaje de error no definido.\n" + # 3. También se pide que se controle cualquier otra excepción que se pueda producir y muestre el mensaje: + # "\n*ERROR* Problemas al mostrar error!\n{e}\n" + # 4. En esta función los mensajes de error deben mostrarse con print. + if msj_error != None: + print(f"\n*ERROR* {MENSAJES_ERROR[indice_error].format(error = msj_error)}\n") + else: + print(f"\n*ERROR* {MENSAJES_ERROR[indice_error]}\n") + + + +def sumar(num1: float, num2: float) -> int: + # TODO: Realizar el desarrollo completo, incluida la documentación... recibe 2 números float y retorna la suma de ambos. + """ + Suma los 2 número recibidos y retorna la suma de ambos. + + Args: + num1 (float): Primer número recibido en la función. + num2 (float): Segundo número recibido en la función. + + Returns: + int: Resultado de la suma de los 2 valores introducidos. + """ + suma = round(num1) + round(num2) + + return suma + + + +def restar(num1: float, num2: float) -> int: + # TODO: Realizar el desarrollo completo, incluida la documentación... recibe 2 números float y retorna la resta de ambos. + """ + Resta los 2 número recibidos y retorna la resta de ambos. + + Args: + num1 (float): Primer número recibido en la función. + num2 (float): Segundo número recibido en la función. + + Returns: + int: Resultado de la resta de los 2 valores introducidos. + """ + resta = round(num1) - round(num2) + + return resta + +def es_resultado_negativo(num1: float, num2: float) -> bool: + """Determina si el resultado de una operación entre num1 y num2 debe ser negativo. + + Args: + num1 (float): Primer número recibido en la función para ser comprobado. + num2 (float): Segundo número recibido en la función para ser comprobado. + + Returns: + bool: Es 'True' si alguno de los números recibidos es negativo y 'False' en caso contrario. + + """ + # TODO: Realizar el desarrollo completo de esta función teniendo en cuenta la documentación y completándola también. + # Debe pasar las pruebas unitarias. + # Se trata de una función que os puede venir bien para utilizarla tanto en la función multiplicar, como en dividir. + # Ya que va a determinar si la multiplicación o división entre dos números debería ser de signo negativo o no. + if num1 < 0 and num2 <0: + negativo = False + elif num1 < 0: + negativo = True + num1 *= -1 + elif num2 < 0: + negativo = True + num2 *= -1 + else: + negativo = False + + return negativo, num1, num2 + + + +def multiplicar(num1: float, num2: float) -> int: + """ + Realiza la multiplicación ENTERA de dos números usando solo sumas y restas. + + Args: + num1 (float): Primer número. + num2 (float): Segundo número. + + Returns: + int: Resultado de la multiplicación. + + Note: + Debe redondear los números recibidos a enteros para trabajar. + """ + # TODO: + # 1. Realizar el desarrollo completo de esta función teniendo en cuenta la documentación. + # 2. Esta función debe pasar las pruebas unitarias. + # 3. No podéis usar el operador de multiplicación de Python para realizar el desarrollo de la misma, + # es decir, que debéis realizar la multiplicación con SUMAS y/o RESTAS... + # 4. Aunque se reciben números de tipo float, debéis redondearlos como números enteros para + # simplificar esta función, es decir, la operación 4.98 * 3.33, deberá convertirse en 5 * 3. + # 5. Tened en cuenta que podéis recibir números negativos, es decir, la operación -5 * -5 = 25 + # 6. OBLIGATORIO usar un bucle for. + # 7. Incluir algún comentario para mejorar la claridad y permitir que otros comprendan el propósito y funcionamiento del código. + + #Llamada a es_resultado_negativo() para comprobar si existen negativos y actualizar num1, num2 + negativo, num1, num2 = es_resultado_negativo(num1, num2) + multiplicacion = 0 + + #Bucle for que suma uno de los numeros tantas veces como el valor del otro numero. + #Existen 2 bucles debido a que se compara previamente que numero es menor para evitar la recurrencia de operaciones. + #Ejemplo: No es lo mismo '2 * 5 = 2 + 2 + 2 + 2 + 2 = 10' que '5 * 2 = 5 + 5 = 10' + if num2 < num1: + for i in range (0, round(num2)): + multiplicacion += round(num1) + else: + for i in range (0, round(num1)): + multiplicacion += round(num2) + + if negativo: + multiplicacion *= -1 + + return multiplicacion + + + +def dividir(num1: float, num2: float) -> int: + """ + Realiza la división ENTERA de dos números usando solo sumas y restas. + + Args: + num1 (float): Dividendo. + num2 (float): Divisor. + + Returns: + int: Resultado de la división. + + Raises: + ZeroDivisionError: Si el divisor es cero. + + Note: + Debe redondear los números recibidos a enteros para trabajar. + """ + # TODO: + # 1. Realizar el desarrollo completo de esta función teniendo en cuenta la documentación. + # 2. Esta función debe pasar las pruebas unitarias. + # 3. No podéis usar el operador de división de Python para realizar el desarrollo de la misma, + # es decir, que debéis realizar la división con SUMAS y/o RESTAS... + # 4. Aunque se reciben números de tipo float, debéis redondearlos como números enteros para + # simplificar esta función, es decir, la operación 4.98 / 3.33, deberá convertirse en 5 / 3. + # 5. Tened en cuenta que podéis recibir números negativos, es decir, la operación -5 / -5 = 1 + # 6. Incluir algún comentario para mejorar la claridad y permitir que otros comprendan el propósito y funcionamiento del código. + negativo, num1, num2 = es_resultado_negativo(num1, num2) + division = num1 + dividiendo = True + cociente = 0 + + while dividiendo: + if (division - round(num2)) >= 0: + division -= round(num2) + cociente += 1 + else: + dividiendo = False + + if negativo: + cociente *= -1 + + return cociente + +def potencia(num1: float, num2: float) -> int: + # TODO: + # 1. Realizar el desarrollo completo de esta función y su documentación. + # 2. Esta función debe pasar las pruebas unitarias. + # 3. PREMISAS a tener en cuenta: + # - Cualquier número elevado a 0 da como resultado 1. + # - Para simplificar esta función, vamos a suponer que un número elevado a un + # exponente negativo siempre dará 0 (aunque en realidad no es así matemáticamente) + # 4. Utiliza la función de multiplicar para realizar los cálculos que te + # harán falta en esta función (RECUERDA que no puedes usar directamente los operadores + # de Python para la multiplicación y división). + # 5. Incluir algún comentario para mejorar la claridad y permitir que otros comprendan el propósito y funcionamiento del código. + resultado = num1 + + if round(num2) == 0: + resultado = 1 + else: + for i in range(0, round(num2) - 1): + resultado = multiplicar(resultado, num1) + + return resultado + + +def pedir_entrada(msj: str) -> str: + """ + Pide al usuario una entrada, elimina espacios por delante y por detrás y la convierte a minúsculas. + + Args: + msj (str): Mensaje para solicitar la entrada. + + Returns: + str: Entrada del usuario. + """ + # TODO: El desarrollo de esta función está incompleto, debéis terminarla teniendo en cuenta la documentación + return input(msj).strip().lower() + + +def calcular_operacion(num1: float, num2: float, operador: str) -> float: + """ + Realiza la operación especificada entre num1 y num2 dependiendo del valor del operador. + + Args: + num1 (float): Primer número. + num2 (float): Segundo número. + operador (str): Operador de la operación. + + Returns: + float: Resultado de la operación. + + Raises: + ZeroDivisionError: Si el divisor es cero. + """ + # TODO: El desarrollo de esta función está incompleto... completadla teniendo en cuenta la documentación + # y que debe realizar las llamadas adecuadas a las funciones ya creadas para realizar los distintos + # cálculos... sumar, restar, multiplicar, dividir y potencia. + # IMPORTANTE: Si hacemos caso a la documentación de esta función, NO debéis capturar la excepción + # ZeroDivisionError aquí, sino que hay que dejadla que se propague a la función llamante, realizar_calculo(), + # dónde se os indica cómo realizar la gestión de estos errores. + + if operador == "+": + resultado = sumar(num1, num2) + elif operador == "-": + resultado = restar(num1, num2) + elif operador == "*" or operador == "x": + resultado = multiplicar(num1, num2) + elif operador == ":" or operador == "/": + resultado = dividir(num1, num2) + elif operador == "exp" or operador == "**": + resultado = potencia(num1, num2) + + return resultado + + + +def obtener_operaciones() -> str: + """ + Devuelve una cadena con la lista de operaciones disponibles en la calculadora. + + Returns: + (str): cadena de caracteres con la información de las operaciones disponibles. + """ + #El desarrollo de esta función está incompleto... ver documentación para solucionarla correctamente. + """ + Operaciones disponibles: + ce => Reiniciar resultado a 0 + decimales => Establecer decimales en resultado + cadena vacía + => Pregunta si desea salir + calculo => Iniciar cálculo secuencial + + => Suma + - => Resta + x o * => Multiplicación + / o : => División + ** o exp => Potencia + cancelar => vovler sin actualizar resultado de la calculadora + cadena vacía + => volver actualizando resultado de la calculadora + """ + operaciones_disponibles = ("\nOperaciones disponibles:\n ce => Reiniciar resultado a 0\n decimales => Establecer decimales en resultado\n cadena vacía + => Pregunta si desea salir\n calculo => Iniciar cálculo secuencial\n + => Suma\n - => Resta\n x o * => Multiplicación\n / o : => División\n ** o exp => Potencia\n cancelar => vovler sin actualizar resultado de la calculadora\n cadena vacía + => volver actualizando resultado de la calculadora") + + return operaciones_disponibles + + +def realizar_calculo(decimales: int, resultado_almacenado: float) -> float: + """ + Realiza una secuencia de cálculos solicitando números y operadores al usuario. + + Args: + decimales (int): Número de decimales para el resultado. + resultado_almacenado (float): Valor almacenado en la calculadora. + + Returns: + float: Resultado final del cálculo o None si se cancela. + + Note: + * Dentro de esta función el usuario puede realizar cálculos secuenciales, es decir, + comenzará introduciendo un número, después un operador, y otro número... a partir + de aquí sobre el resultado acumulado, introducirá operador y número para seguir + realizando cálculos (ver ejemplos en README.md de la tarea en el repositorio de GitHub). + * El usuario es guiado para introducir números y operadores secuencialmente + para realizar operaciones básicas. + * El usuario puede utilizar "resultado" en la secuencia de cálculo para reutilizar el + resultado almacenado en la calculadora. + * El cálculo finaliza al pulsar , volviendo y actualizando el resultado almacenado + de la calculadora con el cálculo realizado. + * También podemos escribir "cancelar", volviendo sin realizar ningún cambio en el + resultado almacenado de la calculadora. + """ + # TODO: El desarrollo de esta función está incompleto... ver documentación para solucionarla correctamente. + + operador = None + resultado = None + realizando_calculos = True + + + + while realizando_calculos: + limpiar_pantalla() + print("\n## Ingrese número, operador, 'resultado', 'cancelar' o para finalizar el cálculo ##\n") + entrada = pedir_entrada(f"\t (Cálculo = {resultado} >> ") + + if entrada == "cancelar": + realizando_calculos = False + + elif entrada == "": + realizando_calculos = False + + elif entrada in OPERADORES: + operador = entrada + + else: + # TODO: este código funciona cuando solucionéis que reconozca las variables resultado_almacenado y decimales. + # Pero no gestiona los posibles tipos de Excepciones que se pueden producir: + # - ValueError que debe mostrar el error que está en la posición 2 de MENSAJES_ERROR. + # - ZeroDivisionError que debe mostrar el error que está en la posición 5 de MENSAJES_ERROR. + # - Exception que debe mostrar el error que está en la posición 6 de MENSAJES_ERROR. + if entrada == "resultado": + entrada = resultado_almacenado + + try: + numero = float(entrada) + + if operador is not None: + if resultado is None: + resultado = 0 + resultado = round(calcular_operacion(resultado, numero, operador), decimales) + operador = None + + elif resultado is None: + resultado = numero + + else: + mostrar_error(3) + except ValueError as e: + mostrar_error(2) + except ZeroDivisionError as e: + mostrar_error(5) + except Exception as e: + mostrar_error(6, e) + + return resultado + + + +def main(): + """ + Función principal de la calculadora. Gestiona la entrada del usuario y coordina las operaciones. + + Note: + El flujo del programa es el siguiente: + + 1. Inicia la calculadora mostrando el resultado almacenado por defecto (0.00). + + 2. El usuario ingresa un comando, que puede ser: + - "lista" para ver todas las operaciones disponibles. + - "ce" para reiniciar el resultado almacenado a 0. + - "decimales " para establecer el número de decimales mostrados en el resultado. + - "calculo" para iniciar una secuencia de cálculo paso a paso. + - Una entrada vacía y pulsa la tecla para salir de la calculadora. + + 3. Según el comando ingresado: + - El programa realiza la operación o ejecuta la acción indicada. + - Al ingresar "calculo": + * El usuario es guiado para introducir números y operadores secuencialmente para realizar operaciones básicas. + * El usuario puede utilizar "resultado" en la secuencia de cálculo para reutilizar el resultado almacenado en la calculadora. + * El cálculo finaliza al pulsar , volviendo y actualizando el resultado almacenado de la calculadora con el cálculo realizado. + * También podemos escribir "cancelar", volviendo sin realizar ningún cambio en el resultado almacenado de la calculadora. + + 4. La calculadora sigue ejecutándose hasta que el usuario confirma la salida al ingresar una entrada vacía y pulsar . + + 5. Finalmente, se limpia la pantalla, el programa se despide con el mensaje "\n\nBye, bye...\n\n" y termina. + """ + # TODO: Corrige los errores y haz que el main funcione correctamente... + + decimales = 2 + resultado = 0.0 + desea_salir = False + limpiar_pantalla() + + while not desea_salir: + entrada = pedir_entrada(f"Operación (RES => {resultado:.{decimales}f}) >> ") + + if entrada == "": + entrada_salir = pedir_entrada("¿Desea salir de la calculadora? (s/n) ") + if entrada_salir == "s": + desea_salir = True + elif entrada_salir != "n": + mostrar_error(4) + elif entrada == "lista": + print(obtener_operaciones()) + + elif entrada == "ce": + resultado = 0.0 + + elif entrada.startswith("decimales"): + # Extraemos las posiciones decimales y las convertimos a un valor entero + if entrada == "decimales": + mostrar_error(1) + else: + decimales = str(entrada.split()[1]) + print(f"Decimales configurados a {decimales}.") + + elif entrada == "calculo": + resultado = realizar_calculo(decimales, resultado) + + else: + mostrar_error(4) + + + +if __name__ == "__main__": + main() diff --git a/dam1_calculadora/pablo_calculadora_alumnos.py b/dam1_calculadora/pablo_calculadora_alumnos.py new file mode 100644 index 0000000..d123170 --- /dev/null +++ b/dam1_calculadora/pablo_calculadora_alumnos.py @@ -0,0 +1,520 @@ +# TODO: ATENCIÓN!!! +# 1. Importar los paquetes necesarios +# 2. Usar la función mostrar_error para imprimir los errores por consola. +# 3. Los comentarios TODO os ayudan a resolver cada apartado de esta prueba. +# 4. CADA función SOLO puede tener una instrucción return. +# 5. En test_calculadora_alumnos.py crear el test unitario para la función es_resultado_negativo y hacer que todos los test unitarios se cumplan. + +import os + +# Mensajes de error predefinidos +MENSAJES_ERROR = ( + "Problemas al intentar limpiar la pantalla {error}", + "Error al configurar los decimales. Formato: decimales .", + "Entrada no válida. Ingrese número, operador, 'resultado', 'cancelar' o para finalizar el cálculo.", + "Error: Introduzca un operador antes de otro número.", + "Comando no reconocido. Escriba 'lista' para ver las operaciones disponibles.", + "Error: no es posible la división por 0! Introduzca otro valor diferente a 0...", + "Se produjo un error: {error}" +) + +# Operadores soportados por la calculadora +OPERADORES = "+", "-", "x", "*", "/", ":", "**", "exp" +# TODO: Ayuda: incluye en esta constante los símbolos que reconocerá la aplicación para el cálculo de las operaciones: +# '+' para la suma. +# '-' para la resta. +# 'x' o '*' para la multiplicación. +# '/' o ':' para la división. +# '**' o 'exp' para la potencia. + + + +def limpiar_pantalla(): + """ + Limpia la consola según el sistema operativo. + """ + # TODO: El desarrollo de esta función está incompleto y con errores. + try: + os.system("clear" if os.name == "posix" else "cls") + # Otra forma de expresar la misma instrucción sería la siguiente: + # if os.name = posix: + # os.system(clear) + # else: + # os.system(cls) + except Exception as e: + mostrar_error(0, e) + + + +def pausa(): + """ + Pausa la ejecución del programa hasta que se pulse ENTER... "\nPresione ENTER para continuar..." + """ + # TODO: Desarrollar esta función según su documentación. + input("\nPresione ENTER para continuar...") + + +def mostrar_error(indice_error: int, msj_error = None): + """ + Muestra un mensaje de error en la consola. + + Args: + indice_error (int): Índice del mensaje de error en MENSAJES_ERROR. + msj_error (str, opcional): Texto adicional para personalizar el mensaje de error. + """ + # TODO: + # 1. Corrige el error que existe a la hora de acceder a los mensajes de error que están en la constante + # MENSAJES_ERROR. A los elementos de una tupla se accede igual que a los caracteres de una + # cadena de caracteres. + # 2. Completa el código de esta función para que controle específicamente las excepciones IndexError y + # muestre el mensaje: "\n*ERROR* Mensaje de error no definido.\n" + # 3. También se pide que se controle cualquier otra excepción que se pueda producir y muestre el mensaje: + # "\n*ERROR* Problemas al mostrar error!\n{e}\n" + # 4. En esta función los mensajes de error deben mostrarse con print. + try: + if msj_error != None: + print(f"\n*ERROR* {MENSAJES_ERROR[indice_error].format(error = msj_error)}\n") + else: + print(f"\n*ERROR* {MENSAJES_ERROR[indice_error]}\n") + except IndexError: + print("\n*ERROR* Mensaje de error no definido.\n") + except Exception as e: + print(f"\n*ERROR* Problemas al mostrar error!\n{e}\n") + + +def sumar(numero1: float, numero2: float): + """ + Función que suma 2 números + + ARGS: + numero1 (float): Primer número de la suma + numero2 (float): Segundo número de la suma + + RETURNS: + (float): La suma de los dos números introducidos + """ + # TODO: Realizar el desarrollo completo, incluida la documentación... recibe 2 números float y retorna la suma de ambos. + return numero1 + numero2 + + + +def restar(numero1: float, numero2: float): + """ + Función que suma 2 números + + ARGS: + numero1 (float): Primer número de la resta + numero2 (float): Segundo número de la resta + + RETURNS: + (float): La resta de los dos números introducidos + """ + # TODO: Realizar el desarrollo completo, incluida la documentación... recibe 2 números float y retorna la resta de ambos. + return numero1 - numero2 + + +def es_resultado_negativo(num1: float, num2: float) -> bool: + """ + Determina si el resultado de una operación entre num1 y num2 debe ser negativo. + + ARGS: + num1 (float): Primer número + num2 (float): Segundo número + + RETURNS: + True si el resultado de una operación entre ellos es negativo y false si no lo es + + """ + # TODO: Realizar el desarrollo completo de esta función teniendo en cuenta la documentación y completándola también. + # Debe pasar las pruebas unitarias. + # Se trata de una función que os puede venir bien para utilizarla tanto en la función multiplicar, como en dividir. + # Ya que va a determinar si la multiplicación o división entre dos números debería ser de signo negativo o no. + + negativo = False + + if (num1 < 0 and num2 > 0) or (num1 > 0 and num2 < 0): + negativo = True + + return False + + + +def multiplicar(num1:float, num2:float) -> float: + """ + Realiza la multiplicación ENTERA de dos números usando solo sumas y restas. + + Args: + num1 (float): Primer número. + num2 (float): Segundo número. + + Returns: + int: Resultado de la multiplicación. + + Note: + Debe redondear los números recibidos a enteros para trabajar. + """ + # TODO: + # 1. Realizar el desarrollo completo de esta función teniendo en cuenta la documentación. + # 2. Esta función debe pasar las pruebas unitarias. + # 3. No podéis usar el operador de multiplicación de Python para realizar el desarrollo de la misma, + # es decir, que debéis realizar la multiplicación con SUMAS y/o RESTAS... + # 4. Aunque se reciben números de tipo float, debéis redondearlos como números enteros para + # simplificar esta función, es decir, la operación 4.98 * 3.33, deberá convertirse en 5 * 3. + # 5. Tened en cuenta que podéis recibir números negativos, es decir, la operación -5 * -5 = 25 + # 6. OBLIGATORIO usar un bucle for. + # 7. Incluir algún comentario para mejorar la claridad y permitir que otros comprendan el propósito y funcionamiento del código. + + negativo = False + num1 = round(num1) + num2 = round(num2) + + #Este if comprueba si solo uno de los 2 números es negativo para al final del programa escribirlo + if es_resultado_negativo(num1, num2): + negativo = True + + #Este if comprueba si el primer número es negativo para transformarlo a positivo + if num1 < 0: + num1 = restar(0, num1) + + #Este if comprueba si el segundo número es negatibo para transformarlo a positivo + if num2 < 0: + num2 = restar(0, num2) + + for i in range(1, num2): + num1 = sumar(num1, num1) + + if negativo == True: + num1 = restar(0, num1) + + return num1 + +def dividir(num1: float, num2: float) -> float: + """ + Realiza la división ENTERA de dos números usando solo sumas y restas. + + Args: + num1 (float): Dividendo. + num2 (float): Divisor. + + Returns: + int: Resultado de la división. + + Raises: + ZeroDivisionError: Si el divisor es cero. + + Note: + Debe redondear los números recibidos a enteros para trabajar. + """ + # TODO: + # 1. Realizar el desarrollo completo de esta función teniendo en cuenta la documentación. + # 2. Esta función debe pasar las pruebas unitarias. + # 3. No podéis usar el operador de división de Python para realizar el desarrollo de la misma, + # es decir, que debéis realizar la división con SUMAS y/o RESTAS... + # 4. Aunque se reciben números de tipo float, debéis redondearlos como números enteros para + # simplificar esta función, es decir, la operación 4.98 / 3.33, deberá convertirse en 5 / 3. + # 5. Tened en cuenta que podéis recibir números negativos, es decir, la operación -5 / -5 = 1 + # 6. Incluir algún comentario para mejorar la claridad y permitir que otros comprendan el propósito y funcionamiento del código. + + negativo = False + + if num2 == 0: + raise ZeroDivisionError + + resultado = 0 + num1 = round(num1) + num2 = round(num2) + + #Este if comprueba si solo uno de los 2 números es negativo para al final del programa escribirlo + if es_resultado_negativo(num1, num2): + negativo = True + + #Este if comprueba si el primer número es negativo para transformarlo a positivo + if num1 < 0: + num1 = restar(0, num1) + + #Este if comprueba si el segundo número es negatibo para transformarlo a positivo + if num2 < 0: + num2 = restar(0, num2) + + while num1 >= num2: + num1 = restar(num1, num2) + resultado = sumar(resultado, 1) + + if negativo == True: + num1 = restar(0, num1) + + return resultado + + +def potencia(num1: float, num2: float): + """ + Realiza la potencia del número num1 + + ARGS: + num1 (float): Es el número al que hay que hacerle la potencia + num2 (float): La potencia del número + + RETURNS: + (float): El número num1 elevado a la potencia num2 + + """ + # TODO: + # 1. Realizar el desarrollo completo de esta función y su documentación. + # 2. Esta función debe pasar las pruebas unitarias. + # 3. PREMISAS a tener en cuenta: + # - Cualquier número elevado a 0 da como resultado 1. + # - Para simplificar esta función, vamos a suponer que un número elevado a un + # exponente negativo siempre dará 0 (aunque en realidad no es así matemáticamente) + # 4. Utiliza la función de multiplicar para realizar los cálculos que te + # harán falta en esta función (RECUERDA que no puedes usar directamente los operadores + # de Python para la multiplicación y división). + # 5. Incluir algún comentario para mejorar la claridad y permitir que otros comprendan el propósito y funcionamiento del código. + + num1 = round(num1) + num2 = round(num2) + resultado = 0 + + if num2 == 0: + resultado = 1 + elif num2 < 0: + resultado = 0 + else: + for i in range(num2): + resultado = sumar(resultado, multiplicar(num1, num1)) + + return resultado + + +def pedir_entrada(msj: str) -> str: + """ + Pide al usuario una entrada, elimina espacios por delante y por detrás y la convierte a minúsculas. + + Args: + msj (str): Mensaje para solicitar la entrada. + + Returns: + str: Entrada del usuario. + """ + # TODO: El desarrollo de esta función está incompleto, debéis terminarla teniendo en cuenta la documentación + return input(msj).strip().lower() + + +def calcular_operacion(num1: float, num2: float, operador: str) -> float: + """ + Realiza la operación especificada entre num1 y num2 dependiendo del valor del operador. + + Args: + num1 (float): Primer número. + num2 (float): Segundo número. + operador (str): Operador de la operación. + + Returns: + float: Resultado de la operación. + + Raises: + ZeroDivisionError: Si el divisor es cero. + """ + # TODO: El desarrollo de esta función está incompleto... completadla teniendo en cuenta la documentación + # y que debe realizar las llamadas adecuadas a las funciones ya creadas para realizar los distintos + # cálculos... sumar, restar, multiplicar, dividir y potencia. + # IMPORTANTE: Si hacemos caso a la documentación de esta función, NO debéis capturar la excepción + # ZeroDivisionError aquí, sino que hay que dejadla que se propague a la función llamante, realizar_calculo(), + # dónde se os indica cómo realizar la gestión de estos errores. + + if operador == OPERADORES[0]: + resultado = sumar(num1, num2) + elif operador == OPERADORES[1]: + resultado = restar(num1, num2) + elif operador == OPERADORES[2] or operador == OPERADORES[3]: + resultado = multiplicar(num1, num2) + elif operador == OPERADORES[4] or operador == OPERADORES[5]: + resultado = dividir(num1, num2) + elif operador == OPERADORES[6] or operador == OPERADORES[7]: + resultado = potencia(num1, num2) + + return resultado + + + +def obtener_operaciones() -> str: + """ + Devuelve una cadena con la lista de operaciones disponibles en la calculadora. + + Returns: + (str): cadena de caracteres con la información de las operaciones disponibles. + """ + # TODO: El desarrollo de esta función está incompleto... ver documentación para solucionarla correctamente. + """ + Operaciones disponibles: + ce => Reiniciar resultado a 0 + decimales => Establecer decimales en resultado + cadena vacía + => Pregunta si desea salir + calculo => Iniciar cálculo secuencial + + => Suma + - => Resta + x o * => Multiplicación + / o : => División + ** o exp => Potencia + cancelar => vovler sin actualizar resultado de la calculadora + cadena vacía + => volver actualizando resultado de la calculadora + """ + return("Operaciones disponibles:\n ce => Reiniciar resultado a 0\n decimales => Establecer decimales en resultado\n cadena vacía + => Pregunta si desea salir\n calculo => Iniciar cálculo secuencial\n + => Suma\n - => Resta\n x o * => Multiplicación\n / o : => División\n ** o exp => Potencia\n cancelar => vovler sin actualizar resultado de la calculadora\n cadena vacía + => volver actualizando resultado de la calculadora") + + +def realizar_calculo(decimales: int, resultado_almacenado: float): + """ + Realiza una secuencia de cálculos solicitando números y operadores al usuario. + + Args: + decimales (int): Número de decimales para el resultado. + resultado_almacenado (float): Valor almacenado en la calculadora. + + Returns: + float: Resultado final del cálculo o None si se cancela. + + Note: + * Dentro de esta función el usuario puede realizar cálculos secuenciales, es decir, + comenzará introduciendo un número, después un operador, y otro número... a partir + de aquí sobre el resultado acumulado, introducirá operador y número para seguir + realizando cálculos (ver ejemplos en README.md de la tarea en el repositorio de GitHub). + * El usuario es guiado para introducir números y operadores secuencialmente + para realizar operaciones básicas. + * El usuario puede utilizar "resultado" en la secuencia de cálculo para reutilizar el + resultado almacenado en la calculadora. + * El cálculo finaliza al pulsar , volviendo y actualizando el resultado almacenado + de la calculadora con el cálculo realizado. + * También podemos escribir "cancelar", volviendo sin realizar ningún cambio en el + resultado almacenado de la calculadora. + """ + # TODO: El desarrollo de esta función está incompleto... ver documentación para solucionarla correctamente. + + operador = None + resultado = None + realizando_calculos = True + + print("\n## Ingrese número, operador, 'resultado', 'cancelar' o para finalizar el cálculo ##\n") + + while realizando_calculos: + entrada = pedir_entrada(f"\t (Cálculo = {resultado}) >> ") + + if entrada == "cancelar": + realizando_calculos = False + + elif entrada == "": + resultado_almacenado = resultado + realizando_calculos = False + + elif entrada in OPERADORES: + operador = entrada + + else: + # TODO: este código funciona cuando solucionéis que reconozca las variables resultado_almacenado y decimales. + # Pero no gestiona los posibles tipos de Excepciones que se pueden producir: + # - ValueError que debe mostrar el error que está en la posición 2 de MENSAJES_ERROR. + # - ZeroDivisionError que debe mostrar el error que está en la posición 5 de MENSAJES_ERROR. + # - Exception que debe mostrar el error que está en la posición 6 de MENSAJES_ERROR. + if entrada == "resultado": + entrada = resultado_almacenado + + try: + numero = float(entrada) + + if operador is not None: + if resultado is None: + resultado = 0 + resultado = round(calcular_operacion(resultado, numero, operador), decimales) + operador = None + + elif resultado is None: + resultado = numero + + else: + mostrar_error(3) + except ValueError: + mostrar_error(2) + except ZeroDivisionError: + mostrar_error(5) + except Exception as e: + mostrar_error(6, e) + + return resultado_almacenado + + +def main(): + """ + Función principal de la calculadora. Gestiona la entrada del usuario y coordina las operaciones. + + Note: + El flujo del programa es el siguiente: + + 1. Inicia la calculadora mostrando el resultado almacenado por defecto (0.00). + + 2. El usuario ingresa un comando, que puede ser: + - "lista" para ver todas las operaciones disponibles. + - "ce" para reiniciar el resultado almacenado a 0. + - "decimales " para establecer el número de decimales mostrados en el resultado. + - "calculo" para iniciar una secuencia de cálculo paso a paso. + - Una entrada vacía y pulsa la tecla para salir de la calculadora. + + 3. Según el comando ingresado: + - El programa realiza la operación o ejecuta la acción indicada. + - Al ingresar "calculo": + * El usuario es guiado para introducir números y operadores secuencialmente para realizar operaciones básicas. + * El usuario puede utilizar "resultado" en la secuencia de cálculo para reutilizar el resultado almacenado en la calculadora. + * El cálculo finaliza al pulsar , volviendo y actualizando el resultado almacenado de la calculadora con el cálculo realizado. + * También podemos escribir "cancelar", volviendo sin realizar ningún cambio en el resultado almacenado de la calculadora. + + 4. La calculadora sigue ejecutándose hasta que el usuario confirma la salida al ingresar una entrada vacía y pulsar . + + 5. Finalmente, se limpia la pantalla, el programa se despide con el mensaje "\n\nBye, bye...\n\n" y termina. + """ + # TODO: Corrige los errores y haz que el main funcione correctamente... + + decimales = 2 + resultado = 0.0 + desea_salir = False + + while not desea_salir: + + limpiar_pantalla() + entrada = pedir_entrada(f"Operación (RES => {resultado}) >> ") + + if entrada == "": + limpiar_pantalla() + entrada = pedir_entrada("¿Desea salir de la calculadora? (s/n) ") + if entrada.strip().lower() == "s": + desea_salir = True + + elif entrada == "lista": + print(obtener_operaciones()) + + pausa() + limpiar_pantalla() + + elif entrada == "ce": + resultado = 0 + limpiar_pantalla() + + elif entrada.startswith("decimales"): + # Extraemos las posiciones decimales y las convertimos a un valor entero + try: + decimales = str(entrada.split()[1]) + print(f"Decimales configurados a {decimales}.") + except: + mostrar_error(1) + pausa() + limpiar_pantalla() + + elif entrada == "calculo": + resultado = realizar_calculo(decimales, resultado) + limpiar_pantalla() + + else: + mostrar_error(4) + pausa() + limpiar_pantalla() + + print("Bye bye...") + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/dam1_calculadora/samuel_calculadora_alumnos.py b/dam1_calculadora/samuel_calculadora_alumnos.py new file mode 100644 index 0000000..2f93b5c --- /dev/null +++ b/dam1_calculadora/samuel_calculadora_alumnos.py @@ -0,0 +1,370 @@ +import os +# TODO: ATENCIÓN!!! +# 1. Importar los paquetes necesarios +# 2. Usar la función mostrar_error para imprimir los errores por consola. +# 3. Los comentarios TODO os ayudan a resolver cada apartado de esta prueba. +# 4. CADA función SOLO puede tener una instrucción return. +# 5. En test_calculadora_alumnos.py crear el test unitario para la función es_resultado_negativo y hacer que todos los test unitarios se cumplan. + + + +# Mensajes de error predefinidos +MENSAJES_ERROR = ( + "Problemas al intentar limpiar la pantalla {error}", + "Error al configurar los decimales. Formato: decimales .", + "Entrada no válida. Ingrese número, operador, 'resultado', 'cancelar' o para finalizar el cálculo.", + "Error: Introduzca un operador antes de otro número.", + "Comando no reconocido. Escriba 'lista' para ver las operaciones disponibles.", + "Error: no es posible la división por 0! Introduzca otro valor diferente a 0...", + "Se produjo un error: {error}" +) + +# Operadores soportados por la calculadora +OPERADORES = ( +"TODO: Ayuda: incluye en esta constante los símbolos que reconocerá la aplicación para el cálculo de las operaciones:", +"'+' para la suma.", +"'-' para la resta.", +"'x' o '*' para la multiplicación.", +"'/' o ':' para la división.", +"'**' o 'exp' para la potencia." +) + + +def limpiar_pantalla(): + """ + Limpia la consola según el sistema operativo. + """ + # TODO: El desarrollo de esta función está incompleto y con errores. + try: + if os.name == "posix": + os.system(clear) + else: + os.system("cls") + except Exception as e: + mostrar_error(1) + + + +def pausa(): + """ + Pausa la ejecución del programa hasta que se pulse ENTER... "\nPresione ENTER para continuar..." + """ + # TODO: Desarrollar esta función según su documentación. + input("\nPresione ENTER para continuar...") + +def mostrar_error(indice_error: int, msj_error = None): + """ + Muestra un mensaje de error en la consola. + + Args: + indice_error (int): Índice del mensaje de error en MENSAJES_ERROR. + msj_error (str, opcional): Texto adicional para personalizar el mensaje de error. + """ + # TODO: + # 1. Corrige el error que existe a la hora de acceder a los mensajes de error que están en la constante + # MENSAJES_ERROR. A los elementos de una tupla se accede igual que a los caracteres de una + # cadena de caracteres. + # 2. Completa el código de esta función para que controle específicamente las excepciones IndexError y + # muestre el mensaje: "\n*ERROR* Mensaje de error no definido.\n" + # 3. También se pide que se controle cualquier otra excepción que se pueda producir y muestre el mensaje: + # "\n*ERROR* Mensaje de error no definido.\n{e}\n" + # 4. En esta función los mensajes de error deben mostrarse con print. + try: + if msj_error != None: + print(f"\n*ERROR* {MENSAJES_ERROR[indice_error].format(error = msj_error)} Mensaje de error no definido.\n") + except Exception: + print(f"\n*ERROR* {MENSAJES_ERROR}Mensaje de error no definido.\n{e}\n") + pausa(2) + +def sumar(): + # TODO: Realizar el desarrollo completo, incluida la documentación... recibe 2 números float y retorna la suma de ambos. + + + +def restar(): + # TODO: Realizar el desarrollo completo, incluida la documentación... recibe 2 números float y retorna la resta de ambos. + + + +def es_resultado_negativo(num1: float, num2: float) -> bool: + """Determina si el resultado de una operación entre num1 y num2 debe ser negativo.""" + # TODO: Realizar el desarrollo completo de esta función teniendo en cuenta la documentación y completándola también. + # Debe pasar las pruebas unitarias. + # Se trata de una función que os puede venir bien para utilizarla tanto en la función multiplicar, como en dividir. + # Ya que va a determinar si la multiplicación o división entre dos números debería ser de signo negativo o no. + + + +def multiplicar(): + """ + Realiza la multiplicación ENTERA de dos números usando solo sumas y restas. + + Args: + num1 (float): Primer número. + num2 (float): Segundo número. + + Returns: + int: Resultado de la multiplicación. + + Note: + Debe redondear los números recibidos a enteros para trabajar. + """ + # TODO: + # 1. Realizar el desarrollo completo de esta función teniendo en cuenta la documentación. + # 2. Esta función debe pasar las pruebas unitarias. + # 3. No podéis usar el operador de multiplicación de Python para realizar el desarrollo de la misma, + # es decir, que debéis realizar la multiplicación con SUMAS y/o RESTAS... + # 4. Aunque se reciben números de tipo float, debéis redondearlos como números enteros para + # simplificar esta función, es decir, la operación 4.98 * 3.33, deberá convertirse en 5 * 3. + # 5. Tened en cuenta que podéis recibir números negativos, es decir, la operación -5 * -5 = 25 + # 6. OBLIGATORIO usar un bucle for. + # 7. Incluir algún comentario para mejorar la claridad y permitir que otros comprendan el propósito y funcionamiento del código. + for i in range(): + + +def dividir(): + """ + Realiza la división ENTERA de dos números usando solo sumas y restas. + + Args: + num1 (float): Dividendo. + num2 (float): Divisor. + + Returns: + int: Resultado de la división. + + Raises: + ZeroDivisionError: Si el divisor es cero. + + Note: + Debe redondear los números recibidos a enteros para trabajar. + """ + # TODO: + # 1. Realizar el desarrollo completo de esta función teniendo en cuenta la documentación. + # 2. Esta función debe pasar las pruebas unitarias. + # 3. No podéis usar el operador de división de Python para realizar el desarrollo de la misma, + # es decir, que debéis realizar la división con SUMAS y/o RESTAS... + # 4. Aunque se reciben números de tipo float, debéis redondearlos como números enteros para + # simplificar esta función, es decir, la operación 4.98 / 3.33, deberá convertirse en 5 / 3. + # 5. Tened en cuenta que podéis recibir números negativos, es decir, la operación -5 / -5 = 1 + # 6. Incluir algún comentario para mejorar la claridad y permitir que otros comprendan el propósito y funcionamiento del código. + + +def potencia(): + # TODO: + # 1. Realizar el desarrollo completo de esta función y su documentación. + # 2. Esta función debe pasar las pruebas unitarias. + # 3. PREMISAS a tener en cuenta: + # - Cualquier número elevado a 0 da como resultado 1. + # - Para simplificar esta función, vamos a suponer que un número elevado a un + # exponente negativo siempre dará 0 (aunque en realidad no es así matemáticamente) + # 4. Utiliza la función de multiplicar para realizar los cálculos que te + # harán falta en esta función (RECUERDA que no puedes usar directamente los operadores + # de Python para la multiplicación y división). + # 5. Incluir algún comentario para mejorar la claridad y permitir que otros comprendan el propósito y funcionamiento del código. + + +def pedir_entrada(msj: str) -> str: + """ + Pide al usuario una entrada, elimina espacios por delante y por detrás y la convierte a minúsculas. + + Args: + msj (str): Mensaje para solicitar la entrada. + + Returns: + str: Entrada del usuario. + """ + # TODO: El desarrollo de esta función está incompleto, debéis terminarla teniendo en cuenta la documentación + return str(msj) + + +def calcular_operacion(num1: float, num2: float, operador: str) -> float: + """ + Realiza la operación especificada entre num1 y num2 dependiendo del valor del operador. + + Args: + num1 (float): Primer número. + num2 (float): Segundo número. + operador (str): Operador de la operación. + + Returns: + float: Resultado de la operación. + + Raises: + ZeroDivisionError: Si el divisor es cero. + """ + # TODO: El desarrollo de esta función está incompleto... completadla teniendo en cuenta la documentación + # y que debe realizar las llamadas adecuadas a las funciones ya creadas para realizar los distintos + # cálculos... sumar, restar, multiplicar, dividir y potencia. + # IMPORTANTE: Si hacemos caso a la documentación de esta función, NO debéis capturar la excepción + # ZeroDivisionError aquí, sino que hay que dejadla que se propague a la función llamante, realizar_calculo(), + # dónde se os indica cómo realizar la gestión de estos errores. + + return resultado, num1, num2, operador + + + +def obtener_operaciones() -> str: + """ + Devuelve una cadena con la lista de operaciones disponibles en la calculadora. + + Returns: + (str): cadena de caracteres con la información de las operaciones disponibles. + """ + # TODO: El desarrollo de esta función está incompleto... ver documentación para solucionarla correctamente. + """ + Operaciones disponibles: + ce => Reiniciar resultado a 0 + decimales => Establecer decimales en resultado + cadena vacía + => Pregunta si desea salir + calculo => Iniciar cálculo secuencial + + => Suma + - => Resta + x o * => Multiplicación + / o : => División + ** o exp => Potencia + cancelar => vovler sin actualizar resultado de la calculadora + cadena vacía + => volver actualizando resultado de la calculadora + """ + print("Operaciones disponibles:") + print("ce => Reiniciar resultado a 0") + print("decimales => Establecer decimales en resultado") + print("cadena vacía + => Pregunta si desea salir") + print("calculo => Iniciar cálculo secuencial") + print("+ => Suma") + print("- => Resta") + print("x o * => Multiplicación") + print("** o exp => Potencia") + print("cancelar => vovler sin actualizar resultado de la calculadora") + print("cadena vacía + => volver actualizando resultado de la calculadora") + + +def realizar_calculo(decimales , resultado): + """ + Realiza una secuencia de cálculos solicitando números y operadores al usuario. + + Args: + decimales (int): Número de decimales para el resultado. + resultado_almacenado (float): Valor almacenado en la calculadora. + + Returns: + float: Resultado final del cálculo o None si se cancela. + + Note: + * Dentro de esta función el usuario puede realizar cálculos secuenciales, es decir, + comenzará introduciendo un número, después un operador, y otro número... a partir + de aquí sobre el resultado acumulado, introducirá operador y número para seguir + realizando cálculos (ver ejemplos en README.md de la tarea en el repositorio de GitHub). + * El usuario es guiado para introducir números y operadores secuencialmente + para realizar operaciones básicas. + * El usuario puede utilizar "resultado" en la secuencia de cálculo para reutilizar el + resultado almacenado en la calculadora. + * El cálculo finaliza al pulsar , volviendo y actualizando el resultado almacenado + de la calculadora con el cálculo realizado. + * También podemos escribir "cancelar", volviendo sin realizar ningún cambio en el + resultado almacenado de la calculadora. + """ + # TODO: El desarrollo de esta función está incompleto... ver documentación para solucionarla correctamente. + + operador = None + resultado = None + realizando_calculos = True + resultado_almacenado = resultado + print("\n## Ingrese número, operador, 'resultado', 'cancelar' o para finalizar el cálculo ##\n") + + while realizando_calculos: + entrada = pedir_entrada(f"\t (Cálculo = resultado) >> ") + + if entrada == "cancelar": + + + elif entrada == "": + + + elif entrada in OPERADORES: + operador = OPERADORES + + else: + # TODO: este código funciona cuando solucionéis que reconozca las variables resultado_almacenado y decimales. + # Pero no gestiona los posibles tipos de Excepciones que se pueden producir: + # - ValueError que debe mostrar el error que está en la posición 2 de MENSAJES_ERROR. + # - ZeroDivisionError que debe mostrar el error que está en la posición 5 de MENSAJES_ERROR. + # - Exception que debe mostrar el error que está en la posición 6 de MENSAJES_ERROR. + if entrada == "resultado": + entrada = resultado_almacenado + + try: + numero = float(entrada) + + if operador is not None: + if resultado is None: + resultado = 0 + resultado = round(calcular_operacion(resultado, numero, operador), decimales) + operador = None + print(MENSAJES_ERROR(2)) + elif resultado is None: + resultado = numero + print(MENSAJES_ERROR(5)) + else: + mostrar_error(3) + except: + print(MENSAJES_ERROR(6)) + + +def main(): + """ + Función principal de la calculadora. Gestiona la entrada del usuario y coordina las operaciones. + + Note: + El flujo del programa es el siguiente: + + 1. Inicia la calculadora mostrando el resultado almacenado por defecto (0.00). + + 2. El usuario ingresa un comando, que puede ser: + - "lista" para ver todas las operaciones disponibles. + - "ce" para reiniciar el resultado almacenado a 0. + - "decimales " para establecer el número de decimales mostrados en el resultado. + - "calculo" para iniciar una secuencia de cálculo paso a paso. + - Una entrada vacía y pulsa la tecla para salir de la calculadora. + + 3. Según el comando ingresado: + - El programa realiza la operación o ejecuta la acción indicada. + - Al ingresar "calculo": + * El usuario es guiado para introducir números y operadores secuencialmente para realizar operaciones básicas. + * El usuario puede utilizar "resultado" en la secuencia de cálculo para reutilizar el resultado almacenado en la calculadora. + * El cálculo finaliza al pulsar , volviendo y actualizando el resultado almacenado de la calculadora con el cálculo realizado. + * También podemos escribir "cancelar", volviendo sin realizar ningún cambio en el resultado almacenado de la calculadora. + + 4. La calculadora sigue ejecutándose hasta que el usuario confirma la salida al ingresar una entrada vacía y pulsar . + + 5. Finalmente, se limpia la pantalla, el programa se despide con el mensaje "\n\nBye, bye...\n\n" y termina. + """ + # TODO: Corrige los errores y haz que el main funcione correctamente... + + decimales = 2 + resultado = 0.0 + desea_salir = False + entrada = pedir_entrada() + while not desea_salir: + + pedir_entrada(f"Operación (RES => resultado) >> ") + + if entrada == "": + pedir_entrada("¿Desea salir de la calculadora? (s/n) ") + + elif entrada == "lista": + obtener_operaciones() + + elif entrada == "ce": + limpiar_pantalla() + + elif entrada.startswith("decimales"): + # Extraemos las posiciones decimales y las convertimos a un valor entero + decimales = str(entrada.split()[1]) + print(f"Decimales configurados a {decimales}.") + + elif entrada == "calculo": + realizar_calculo(decimales, resultado) + + else: + mostrar_error() diff --git a/dam1_calculadora/sergio_calculadora_alumnos.py b/dam1_calculadora/sergio_calculadora_alumnos.py new file mode 100644 index 0000000..3a78d5a --- /dev/null +++ b/dam1_calculadora/sergio_calculadora_alumnos.py @@ -0,0 +1,445 @@ +# TODO: ATENCIÓN!!! +# 1. Importar los paquetes necesarios +# 2. Usar la función mostrar_error para imprimir los errores por consola. +# 3. Los comentarios TODO os ayudan a resolver cada apartado de esta prueba. +# 4. CADA función SOLO puede tener una instrucción return. +# 5. En test_calculadora_alumnos.py crear el test unitario para la función es_resultado_negativo y hacer que todos los test unitarios se cumplan. + + + +# Mensajes de error predefinidos +MENSAJES_ERROR = ( + "Problemas al intentar limpiar la pantalla {error}", + "Error al configurar los decimales. Formato: decimales .", + "Entrada no válida. Ingrese número, operador, 'resultado', 'cancelar' o para finalizar el cálculo.", + "Error: Introduzca un operador antes de otro número.", + "Comando no reconocido. Escriba 'lista' para ver las operaciones disponibles.", + "Error: no es posible la división por 0! Introduzca otro valor diferente a 0...", + "Se produjo un error: {error}" +) + +# Operadores soportados por la calculadora +OPERADORES = +# TODO: Ayuda: incluye en esta constante los símbolos que reconocerá la aplicación para el cálculo de las operaciones: +# '+' para la suma. +# '-' para la resta. +# 'x' o '*' para la multiplicación. +# '/' o ':' para la división. +# '**' o 'exp' para la potencia. + +def sumar(a, b): + return a + b + +def restar(a, b): + return a - b + +def multiplicar(a, b): + return a * b + +def dividir(a, b): + return a / b + +def potencia(a, b): + return a ** b + return None + + + +import os + +def limpiar_pantalla(): + """ + Limpia la consola según el sistema operativo. + """ + # TODO: El desarrollo de esta función está incompleto y con errores. + + if os.name(clear if os.name = posix else cls) + # Otra forma de expresar la misma instrucción sería la siguiente: + # if os.name = posix: + # os.system(clear) + # else: + # os.system(cls) + + if os.name == 'nt': + os.system('cls') + else: + os.system('clear') + + except Exception as e: + + + + + +def pausa(): + """ + Pausa la ejecución del programa hasta que se pulse ENTER... "\nPresione ENTER para continuar..." + """ + # TODO: Desarrollar esta función según su documentación. + +import os + os.system("Pause") + input("Presione ENTER para continuar...") + + + +def mostrar_error(indice_error: int, msj_error = None): + """ + Muestra un mensaje de error en la consola. + + Args: + indice_error (int): Índice del mensaje de error en MENSAJES_ERROR. + msj_error (str, opcional): Texto adicional para personalizar el mensaje de error. + """ + # TODO: + # 1. Corrige el error que existe a la hora de acceder a los mensajes de error que están en la constante + # MENSAJES_ERROR. A los elementos de una tupla se accede igual que a los caracteres de una + # cadena de caracteres. + # 2. Completa el código de esta función para que controle específicamente las excepciones IndexError y + # muestre el mensaje: "\n*ERROR* Mensaje de error no definido.\n" + # 3. También se pide que se controle cualquier otra excepción que se pueda producir y muestre el mensaje: + # "\n*ERROR* Problemas al mostrar error!\n{e}\n" + # 4. En esta función los mensajes de error deben mostrarse con print. + + if msj_error != None: + print(f"\n*ERROR* {MENSAJES_ERROR.format(error = msj_error)}\n") + else: + print(f"\n*ERROR* {MENSAJES_ERROR}\n") + + except IndexError: + print("\n*ERROR* Mensaje de error no definido.\n") + + except Exception as e: + print(f"\n*ERROR* Problemas al mostrar error!\n{e}\n") + +if else: + + +def sumar(num1: float, num2: float) -> float: + # TODO: Realizar el desarrollo completo, incluida la documentación... recibe 2 números float y retorna la suma de ambos. + + return num1 + num2 + +def restar(num1: float, num2: float) -> float: + # TODO: Realizar el desarrollo completo, incluida la documentación... recibe 2 números float y retorna la resta de ambos. + + return num1 - num2 + + +def es_resultado_negativo(num1: float, num2: float) -> bool: + """Determina si el resultado de una operación entre num1 y num2 debe ser negativo.""" + # TODO: Realizar el desarrollo completo de esta función teniendo en cuenta la documentación y completándola también. + # Debe pasar las pruebas unitarias. + # Se trata de una función que os puede venir bien para utilizarla tanto en la función multiplicar, como en dividir. + # Ya que va a determinar si la multiplicación o división entre dos números debería ser de signo negativo o no. + return (num1 < 0) != (num2 < 0) + + +def multiplicar(): + """ + Realiza la multiplicación ENTERA de dos números usando solo sumas y restas. + + Args: + num1 (float): Primer número. + num2 (float): Segundo número. + + Returns: + int: Resultado de la multiplicación. + + Note: + Debe redondear los números recibidos a enteros para trabajar. + """ + # TODO: + # 1. Realizar el desarrollo completo de esta función teniendo en cuenta la documentación. + # 2. Esta función debe pasar las pruebas unitarias. + # 3. No podéis usar el operador de multiplicación de Python para realizar el desarrollo de la misma, + # es decir, que debéis realizar la multiplicación con SUMAS y/o RESTAS... + # 4. Aunque se reciben números de tipo float, debéis redondearlos como números enteros para + # simplificar esta función, es decir, la operación 4.98 * 3.33, deberá convertirse en 5 * 3. + # 5. Tened en cuenta que podéis recibir números negativos, es decir, la operación -5 * -5 = 25 + # 6. OBLIGATORIO usar un bucle for. + # 7. Incluir algún comentario para mejorar la claridad y permitir que otros comprendan el propósito y funcionamiento del código. + + + +def dividir(): + """ + Realiza la división ENTERA de dos números usando solo sumas y restas. + + Args: + num1 (float): Dividendo. + num2 (float): Divisor. + + Returns: + int: Resultado de la división. + + Raises: + ZeroDivisionError: Si el divisor es cero. + + Note: + Debe redondear los números recibidos a enteros para trabajar. + """ + # TODO: + # 1. Realizar el desarrollo completo de esta función teniendo en cuenta la documentación. + # 2. Esta función debe pasar las pruebas unitarias. + # 3. No podéis usar el operador de división de Python para realizar el desarrollo de la misma, + # es decir, que debéis realizar la división con SUMAS y/o RESTAS... + # 4. Aunque se reciben números de tipo float, debéis redondearlos como números enteros para + # simplificar esta función, es decir, la operación 4.98 / 3.33, deberá convertirse en 5 / 3. + # 5. Tened en cuenta que podéis recibir números negativos, es decir, la operación -5 / -5 = 1 + # 6. Incluir algún comentario para mejorar la claridad y permitir que otros comprendan el propósito y funcionamiento del código. + + +def potencia(): + # TODO: + # 1. Realizar el desarrollo completo de esta función y su documentación. + # 2. Esta función debe pasar las pruebas unitarias. + # 3. PREMISAS a tener en cuenta: + # - Cualquier número elevado a 0 da como resultado 1. + # - Para simplificar esta función, vamos a suponer que un número elevado a un + # exponente negativo siempre dará 0 (aunque en realidad no es así matemáticamente) + # 4. Utiliza la función de multiplicar para realizar los cálculos que te + # harán falta en esta función (RECUERDA que no puedes usar directamente los operadores + # de Python para la multiplicación y división). + # 5. Incluir algún comentario para mejorar la claridad y permitir que otros comprendan el propósito y funcionamiento del código. + + +def pedir_entrada(msj: str) -> str: + """ + Pide al usuario una entrada, elimina espacios por delante y por detrás y la convierte a minúsculas. + + Args: + msj (str): Mensaje para solicitar la entrada. + + Returns: + str: Entrada del usuario. + """ + # TODO: El desarrollo de esta función está incompleto, debéis terminarla teniendo en cuenta la documentación + return input(msj) + + +def calcular_operacion(num1: float, num2: float, operador: str) -> float: + """ + Realiza la operación especificada entre num1 y num2 dependiendo del valor del operador. + + Args: + num1 (float): Primer número. + num2 (float): Segundo número. + operador (str): Operador de la operación. + + Returns: + float: Resultado de la operación. + + Raises: + ZeroDivisionError: Si el divisor es cero. + """ + # TODO: El desarrollo de esta función está incompleto... completadla teniendo en cuenta la documentación + # y que debe realizar las llamadas adecuadas a las funciones ya creadas para realizar los distintos + # cálculos... sumar, restar, multiplicar, dividir y potencia. + # IMPORTANTE: Si hacemos caso a la documentación de esta función, NO debéis capturar la excepción + # ZeroDivisionError aquí, sino que hay que dejadla que se propague a la función llamante, realizar_calculo(), + # dónde se os indica cómo realizar la gestión de estos errores. + + return resultado + + + +def obtener_operaciones() -> str: + """ + Devuelve una cadena con la lista de operaciones disponibles en la calculadora. + + Returns: + (str): cadena de caracteres con la información de las operaciones disponibles. + """ + # TODO: El desarrollo de esta función está incompleto... ver documentación para solucionarla correctamente. + """ + Operaciones disponibles: + ce => Reiniciar resultado a 0 + decimales => Establecer decimales en resultado + cadena vacía + => Pregunta si desea salir + calculo => Iniciar cálculo secuencial + + => Suma + - => Resta + x o * => Multiplicación + / o : => División + ** o exp => Potencia + cancelar => vovler sin actualizar resultado de la calculadora + cadena vacía + => volver actualizando resultado de la calculadora + """ + +def mostrar_operaciones(): + + print("Operaciones disponibles:") + print(" ce => Reiniciar resultado a 0") + print(" decimales => Establecer decimales en resultado") + print(" calculo => Iniciar cálculo secuencial") + print(" + => Suma") + print(" - => Resta") + print(" x o * => Multiplicación") + print(" / o : => División") + print(" ** o exp => Potencia") + print(" cancelar => Volver sin actualizar resultado de la calculadora") + print(" => Volver actualizando resultado de la calculadora") + + + +def realizar_calculo(): + """ + Realiza una secuencia de cálculos solicitando números y operadores al usuario. + + Args: + decimales (int): Número de decimales para el resultado. + resultado_almacenado (float): Valor almacenado en la calculadora. + + Returns: + float: Resultado final del cálculo o None si se cancela. + + Note: + * Dentro de esta función el usuario puede realizar cálculos secuenciales, es decir, + comenzará introduciendo un número, después un operador, y otro número... a partir + de aquí sobre el resultado acumulado, introducirá operador y número para seguir + realizando cálculos (ver ejemplos en README.md de la tarea en el repositorio de GitHub). + * El usuario es guiado para introducir números y operadores secuencialmente + para realizar operaciones básicas. + * El usuario puede utilizar "resultado" en la secuencia de cálculo para reutilizar el + resultado almacenado en la calculadora. + * El cálculo finaliza al pulsar , volviendo y actualizando el resultado almacenado + de la calculadora con el cálculo realizado. + * También podemos escribir "cancelar", volviendo sin realizar ningún cambio en el + resultado almacenado de la calculadora. + """ + # TODO: El desarrollo de esta función está incompleto... ver documentación para solucionarla correctamente. + + operador = None + resultado = None + realizando_calculos = True + + print("\n## Ingrese número, operador, 'resultado', 'cancelar' o para finalizar el cálculo ##\n") + + resultado = 0.0 + while True: + entrada = input(f"Resultado actual: {resultado} \nIngrese operación o 'enter' para finalizar: ") + + if entrada.lower() == "salir": + break + + while realizando_calculos: + entrada = pedir_entrada(f"\t (Cálculo = resultado) >> ") + + if entrada == "cancelar": + + + elif entrada == "": + + + elif entrada in OPERADORES: + operador = + + else: + # TODO: este código funciona cuando solucionéis que reconozca las variables resultado_almacenado y decimales. + # Pero no gestiona los posibles tipos de Excepciones que se pueden producir: + # - ValueError que debe mostrar el error que está en la posición 2 de MENSAJES_ERROR. + # - ZeroDivisionError que debe mostrar el error que está en la posición 5 de MENSAJES_ERROR. + # - Exception que debe mostrar el error que está en la posición 6 de MENSAJES_ERROR. + if entrada == "resultado": + entrada = resultado_almacenado + + try: + numero = float(entrada) + + if operador is not None: + if resultado is None: + resultado = 0 + resultado = round(calcular_operacion(resultado, numero, operador), decimales) + operador = None + + elif resultado is None: + resultado = numero + + else: + mostrar_error(3) + except + + + + + +def main(): + """ + Función principal de la calculadora. Gestiona la entrada del usuario y coordina las operaciones. + + Note: + El flujo del programa es el siguiente: + + 1. Inicia la calculadora mostrando el resultado almacenado por defecto (0.00). + + 2. El usuario ingresa un comando, que puede ser: + - "lista" para ver todas las operaciones disponibles. + - "ce" para reiniciar el resultado almacenado a 0. + - "decimales " para establecer el número de decimales mostrados en el resultado. + - "calculo" para iniciar una secuencia de cálculo paso a paso. + - Una entrada vacía y pulsa la tecla para salir de la calculadora. + + 3. Según el comando ingresado: + - El programa realiza la operación o ejecuta la acción indicada. + - Al ingresar "calculo": + * El usuario es guiado para introducir números y operadores secuencialmente para realizar operaciones básicas. + * El usuario puede utilizar "resultado" en la secuencia de cálculo para reutilizar el resultado almacenado en la calculadora. + * El cálculo finaliza al pulsar , volviendo y actualizando el resultado almacenado de la calculadora con el cálculo realizado. + * También podemos escribir "cancelar", volviendo sin realizar ningún cambio en el resultado almacenado de la calculadora. + + 4. La calculadora sigue ejecutándose hasta que el usuario confirma la salida al ingresar una entrada vacía y pulsar . + + 5. Finalmente, se limpia la pantalla, el programa se despide con el mensaje "\n\nBye, bye...\n\n" y termina. + """ + # TODO: Corrige los errores y haz que el main funcione correctamente... + # Inicio de la aplicación + + + + +if __name__ == "__main__": + + decimales = 2 + resultado = "0.0" + + menu() + +def mostrar_operaciones(): + + print("Operaciones disponibles:") + print(" ce => Reiniciar resultado a 0") + print(" decimales => Establecer decimales en resultado") + print(" calculo => Iniciar cálculo secuencial") + print(" + => Suma") + print(" - => Resta") + print(" x o * => Multiplicación") + print(" / o : => División") + print(" ** o exp => Potencia") + print(" cancelar => Volver sin actualizar resultado de la calculadora") + print(" => Volver actualizando resultado de la calculadora") + + while not desea_salir: + + pedir_entrada(f"Operación (RES => resultado) >> ") + + if entrada == "": + pedir_entrada("¿Desea salir de la calculadora? (s/n) ") + + elif entrada == "lista": + obtener_operaciones + + elif entrada == "ce": + + + elif entrada.startswith("decimales"): + # Extraemos las posiciones decimales y las convertimos a un valor entero + decimales = str(entrada.split()[1]) + print(f"Decimales configurados a {decimales}.") + + elif entrada == "calculo": + realizar_calculo(decimales, resultado) + + else: + mostrar_error + diff --git a/daw1b_calculadora/pablo_calculadora_alumnos.py b/daw1b_calculadora/pablo_calculadora_alumnos.py new file mode 100644 index 0000000..67f3cd4 --- /dev/null +++ b/daw1b_calculadora/pablo_calculadora_alumnos.py @@ -0,0 +1,468 @@ +# TODO: ATENCIÓN!!! +# 1. Importar los paquetes necesarios +# 2. Usar la función mostrar_error para imprimir los errores por consola. +# 3. Los comentarios TODO os ayudan a resolver cada apartado de esta prueba. +# 4. CADA función SOLO puede tener una instrucción return. +# 5. En test_calculadora_alumnos.py crear el test unitario para la función es_resultado_negativo y hacer que todos los test unitarios se cumplan. + + +import os + + +# Mensajes de error predefinidos +MENSAJES_ERROR = ( + "Problemas al intentar limpiar la pantalla {error}", + "Error al configurar los decimales. Formato: decimales .", + "Entrada no válida. Ingrese número, operador, 'resultado', 'cancelar' o para finalizar el cálculo.", + "Error: Introduzca un operador antes de otro número.", + "Comando no reconocido. Escriba 'lista' para ver las operaciones disponibles.", + "Error: no es posible la división por 0! Introduzca otro valor diferente a 0...", + "Se produjo un error: {error}" +) + +# Operadores soportados por la calculadora +# TODO: Ayuda: incluye en esta constante los símbolos que reconocerá la aplicación para el cálculo de las operaciones: + +OPERADORES = ( + '+', + '-', + 'x' '*', + '/' ':', + '**' 'exp', +) + + + + +def limpiar_pantalla(): + """ + Limpia la consola según el sistema operativo. + + En sistemas Windows utiliza el comando 'cls', en Linux o macOS utiliza 'clear' (os.name == "posix"). + """ + try: + # Debe funcionar en todos los sistemas operativos + if os.name == ("posix"): + os.system("clear") + else: + os.system("cls") + except Exception as e: + mostrar_error(f"Problemas al intentar limpiar la pantalla: {e}") + + + + + +def pausa(): + """ + Pausa la ejecución del programa hasta que se pulse ENTER... "\nPresione ENTER para continuar..." + """ + # TODO: Desarrollar esta función según su documentación. + + +def mostrar_error(indice_error: int, msj_error = None): + """ + Muestra un mensaje de error en la consola. + + Args: + indice_error (int): Índice del mensaje de error en MENSAJES_ERROR. + msj_error (str, opcional): Texto adicional para personalizar el mensaje de error. + """ + # TODO: + # 1. Corrige el error que existe a la hora de acceder a los mensajes de error que están en la constante + # MENSAJES_ERROR. A los elementos de una tupla se accede igual que a los caracteres de una + # cadena de caracteres. + # 2. Completa el código de esta función para que controle específicamente las excepciones IndexError y + # muestre el mensaje: "\n*ERROR* Mensaje de error no definido.\n" + # 3. También se pide que se controle cualquier otra excepción que se pueda producir y muestre el mensaje: + # "\n*ERROR* Problemas al mostrar error!\n{e}\n" + # 4. En esta función los mensajes de error deben mostrarse con print. + MENSAJES_ERROR = indice_error + + if msj_error != None: + print(f"\n*ERROR* {MENSAJES_ERROR.format(error = msj_error)}\n") + else: + print(f"\n*ERROR* {MENSAJES_ERROR}\n") + + +def sumar(num1: float, num2: float) -> float: + + """ + Realiza la SUMA de dos números. + + Args: + num1 (float): Primer número. + num2 (float): Segundo número. + + Returns: + float: Resultado de la suma. + + """ + + # TODO: Realizar el desarrollo completo, incluida la documentación... recibe 2 números float y retorna la suma de ambos. + resultado_suma = num1 + num2 + return float(resultado_suma) + + + +def restar(num1: float, num2: float) -> float: + + """ + Realiza la RESTA de dos números. + + Args: + num1 (float): Primer número. + num2 (float): Segundo número. + + Returns: + float: Resultado de la suma. + + """ + # TODO: Realizar el desarrollo completo, incluida la documentación... recibe 2 números float y retorna la resta de ambos. + resultado_resta = num1 - num2 + return float(resultado_resta) + + +def es_resultado_negativo(num1: float, num2: float) -> bool: + """Determina si el resultado de una operación entre num1 y num2 debe ser negativo.""" + # TODO: Realizar el desarrollo completo de esta función teniendo en cuenta la documentación y completándola también. + # Debe pasar las pruebas unitarias. + # Se trata de una función que os puede venir bien para utilizarla tanto en la función multiplicar, como en dividir. + # Ya que va a determinar si la multiplicación o división entre dos números debería ser de signo negativo o no. + # resultado = None + + # if num1 < 0 and num2 < 0: + # resultado + + + + +def multiplicar(): + """ + Realiza la multiplicación ENTERA de dos números usando solo sumas y restas. + + Args: + num1 (float): Primer número. + num2 (float): Segundo número. + + Returns: + int: Resultado de la multiplicación. + + Note: + Debe redondear los números recibidos a enteros para trabajar. + """ + # TODO: + # 1. Realizar el desarrollo completo de esta función teniendo en cuenta la documentación. + # 2. Esta función debe pasar las pruebas unitarias. + # 3. No podéis usar el operador de multiplicación de Python para realizar el desarrollo de la misma, + # es decir, que debéis realizar la multiplicación con SUMAS y/o RESTAS... + # 4. Aunque se reciben números de tipo float, debéis redondearlos como números enteros para + # simplificar esta función, es decir, la operación 4.98 * 3.33, deberá convertirse en 5 * 3. + # 5. Tened en cuenta que podéis recibir números negativos, es decir, la operación -5 * -5 = 25 + # 6. OBLIGATORIO usar un bucle for. + # 7. Incluir algún comentario para mejorar la claridad y permitir que otros comprendan el propósito y funcionamiento del código. + + + +def dividir(): + """ + Realiza la división ENTERA de dos números usando solo sumas y restas. + + Args: + num1 (float): Dividendo. + num2 (float): Divisor. + + Returns: + int: Resultado de la división. + + Raises: + ZeroDivisionError: Si el divisor es cero. + + Note: + Debe redondear los números recibidos a enteros para trabajar. + """ + # TODO: + # 1. Realizar el desarrollo completo de esta función teniendo en cuenta la documentación. + # 2. Esta función debe pasar las pruebas unitarias. + # 3. No podéis usar el operador de división de Python para realizar el desarrollo de la misma, + # es decir, que debéis realizar la división con SUMAS y/o RESTAS... + # 4. Aunque se reciben números de tipo float, debéis redondearlos como números enteros para + # simplificar esta función, es decir, la operación 4.98 / 3.33, deberá convertirse en 5 / 3. + # 5. Tened en cuenta que podéis recibir números negativos, es decir, la operación -5 / -5 = 1 + # 6. Incluir algún comentario para mejorar la claridad y permitir que otros comprendan el propósito y funcionamiento del código. + + +def potencia(): + # TODO: + # 1. Realizar el desarrollo completo de esta función y su documentación. + # 2. Esta función debe pasar las pruebas unitarias. + # 3. PREMISAS a tener en cuenta: + # - Cualquier número elevado a 0 da como resultado 1. + # - Para simplificar esta función, vamos a suponer que un número elevado a un + # exponente negativo siempre dará 0 (aunque en realidad no es así matemáticamente) + # 4. Utiliza la función de multiplicar para realizar los cálculos que te + # harán falta en esta función (RECUERDA que no puedes usar directamente los operadores + # de Python para la multiplicación y división). + # 5. Incluir algún comentario para mejorar la claridad y permitir que otros comprendan el propósito y funcionamiento del código. + pass + +def pedir_entrada(msj: str) -> str: + """ + Pide al usuario una entrada, elimina espacios por delante y por detrás y la convierte a minúsculas. + + Args: + msj (str): Mensaje para solicitar la entrada. + + Returns: + str: Entrada del usuario. + """ + # TODO: El desarrollo de esta función está incompleto, debéis terminarla teniendo en cuenta la documentación + msj = msj.strip().lower() + + return input(str(msj)) + + +def calcular_operacion(num1: float, num2: float, operador: str) -> float: + """ + Realiza la operación especificada entre num1 y num2 dependiendo del valor del operador. + + Args: + num1 (float): Primer número. + num2 (float): Segundo número. + operador (str): Operador de la operación. + + Returns: + float: Resultado de la operación. + + Raises: + ZeroDivisionError: Si el divisor es cero. + """ + # TODO: El desarrollo de esta función está incompleto... completadla teniendo en cuenta la documentación + # y que debe realizar las llamadas adecuadas a las funciones ya creadas para realizar los distintos + # cálculos... sumar, restar, multiplicar, dividir y potencia. + # IMPORTANTE: Si hacemos caso a la documentación de esta función, NO debéis capturar la excepción + # ZeroDivisionError aquí, sino que hay que dejadla que se propague a la función llamante, realizar_calculo(), + # dónde se os indica cómo realizar la gestión de estos errores. + + resultado = None + + if operador == "+": + resultado = sumar(num1, num2) + + elif operador == "-": + resultado = sumar(num1, num2) + + return resultado + + + +def obtener_operaciones() -> str: + """ + Devuelve una cadena con la lista de operaciones disponibles en la calculadora. + + Returns: + (str): cadena de caracteres con la información de las operaciones disponibles. + """ + # TODO: El desarrollo de esta función está incompleto... ver documentación para solucionarla correctamente. + """ + Operaciones disponibles: + ce => Reiniciar resultado a 0 + decimales => Establecer decimales en resultado + cadena vacía + => Pregunta si desea salir + calculo => Iniciar cálculo secuencial + + => Suma + - => Resta + x o * => Multiplicación + / o : => División + ** o exp => Potencia + cancelar => vovler sin actualizar resultado de la calculadora + cadena vacía + => volver actualizando resultado de la calculadora + """ + print("\nOperaciones disponibles:") + print(" ce => Reiniciar resultado a 0") + print(" decimales => Establecer decimales en resultado") + print(" cadena vacía + => Pregunta si desea salir") + print(" calculo => Iniciar cálculo secuencial") + print(" + => Suma") + print(" - => Resta") + print(" x o * => Multiplicación") + print(" / o : => División") + print(" ** o exp => Potencia") + print(" cancelar => vovler sin actualizar resultado de la calculadora") + print(" cadena vacía + => volver actualizando resultado de la calculadora") + + + + + + + + + + +def realizar_calculo(decimales: int, resultado_almacenado: float) -> float: + """ + Realiza una secuencia de cálculos solicitando números y operadores al usuario. + + Args: + decimales (int): Número de decimales para el resultado. + resultado_almacenado (float): Valor almacenado en la calculadora. + + Returns: + float: Resultado final del cálculo o None si se cancela. + + Note: + * Dentro de esta función el usuario puede realizar cálculos secuenciales, es decir, + comenzará introduciendo un número, después un operador, y otro número... a partir + de aquí sobre el resultado acumulado, introducirá operador y número para seguir + realizando cálculos (ver ejemplos en README.md de la tarea en el repositorio de GitHub). + * El usuario es guiado para introducir números y operadores secuencialmente + para realizar operaciones básicas. + * El usuario puede utilizar "resultado" en la secuencia de cálculo para reutilizar el + resultado almacenado en la calculadora. + * El cálculo finaliza al pulsar , volviendo y actualizando el resultado almacenado + de la calculadora con el cálculo realizado. + * También podemos escribir "cancelar", volviendo sin realizar ningún cambio en el + resultado almacenado de la calculadora. + """ + # TODO: El desarrollo de esta función está incompleto... ver documentación para solucionarla correctamente. + + operador = None + resultado = None + realizando_calculos = True + resultado = resultado_almacenado + + print("\n## Ingrese número, operador, 'resultado', 'cancelar' o para finalizar el cálculo ##\n") + + while realizando_calculos: + entrada = pedir_entrada(f"\t (Cálculo = {resultado}) >> ") + + if entrada == "cancelar": + entrada = resultado + + elif entrada == "": + realizando_calculos = True + + elif entrada in OPERADORES: + operador = entrada + + # else: + # # TODO: este código funciona cuando solucionéis que reconozca las variables resultado_almacenado y decimales. + # # Pero no gestiona los posibles tipos de Excepciones que se pueden producir: + # # - ValueError que debe mostrar el error que está en la posición 2 de MENSAJES_ERROR. + # # - ZeroDivisionError que debe mostrar el error que está en la posición 5 de MENSAJES_ERROR. + # # - Exception que debe mostrar el error que está en la posición 6 de MENSAJES_ERROR. + # if entrada == "resultado": + # entrada = resultado_almacenado + + # try: + # numero = float(entrada) + + # if operador is not None: + # if resultado is None: + # resultado = 0 + # resultado = round(calcular_operacion(resultado, numero, operador), decimales) + # operador = None + + # elif resultado is None: + # resultado = numero + + # else: + # mostrar_error(3) + # except: + # mostrar_error() + + else: + # TODO: este código funciona cuando solucionéis que reconozca las variables resultado_almacenado y decimales. + # Pero no gestiona los posibles tipos de Excepciones que se pueden producir: + # - ValueError que debe mostrar el error que está en la posición 2 de MENSAJES_ERROR. + # - ZeroDivisionError que debe mostrar el error que está en la posición 5 de MENSAJES_ERROR. + # - Exception que debe mostrar el error que está en la posición 6 de MENSAJES_ERROR. + if entrada == "resultado": + entrada = resultado_almacenado + + try: + numero = float(entrada) + + if operador is not None: + if resultado is None: + resultado = 0 + resultado = round(calcular_operacion(resultado, numero, operador), decimales) + operador = None + + elif resultado is None: + resultado = numero + + else: + mostrar_error(3) + except ValueError: + mostrar_error(2) + +def main(): + """ + Función principal de la calculadora. Gestiona la entrada del usuario y coordina las operaciones. + + Note: + El flujo del programa es el siguiente: + + 1. Inicia la calculadora mostrando el resultado almacenado por defecto (0.00). + + 2. El usuario ingresa un comando, que puede ser: + - "lista" para ver todas las operaciones disponibles. + - "ce" para reiniciar el resultado almacenado a 0. + - "decimales " para establecer el número de decimales mostrados en el resultado. + - "calculo" para iniciar una secuencia de cálculo paso a paso. + - Una entrada vacía y pulsa la tecla para salir de la calculadora. + + 3. Según el comando ingresado: + - El programa realiza la operación o ejecuta la acción indicada. + - Al ingresar "calculo": + * El usuario es guiado para introducir números y operadores secuencialmente para realizar operaciones básicas. + * El usuario puede utilizar "resultado" en la secuencia de cálculo para reutilizar el resultado almacenado en la calculadora. + * El cálculo finaliza al pulsar , volviendo y actualizando el resultado almacenado de la calculadora con el cálculo realizado. + * También podemos escribir "cancelar", volviendo sin realizar ningún cambio en el resultado almacenado de la calculadora. + + 4. La calculadora sigue ejecutándose hasta que el usuario confirma la salida al ingresar una entrada vacía y pulsar . + + 5. Finalmente, se limpia la pantalla, el programa se despide con el mensaje "\n\nBye, bye...\n\n" y termina. + """ + # TODO: Corrige los errores y haz que el main funcione correctamente... + limpiar_pantalla() + + decimales = 2 + resultado = 0.0 + resultado_almacenado = None + + desea_salir = False + + while not desea_salir: + + entrada = pedir_entrada(f"Operación (RES => {resultado}) >> ") + + if entrada == "": + pedir_entrada("¿Desea salir de la calculadora? (s/n) ") + if entrada == "s" or entrada == "si" or entrada == "": + limpiar_pantalla() + print("\n\nBye Bye...\n\n") + desea_salir = True + + elif entrada == "lista": + obtener_operaciones() + + elif entrada == "ce": + pass + + elif entrada.startswith("decimales"): + # Extraemos las posiciones decimales y las convertimos a un valor entero + decimales = str(entrada.split(".")[:-1]) + print(f"Decimales configurados a {decimales}.") + + elif entrada == "calculo": + resultado_almacenado = realizar_calculo(resultado, resultado_almacenado) + + + else: + mostrar_error(2) + +if __name__ == "__main__": + main() \ No newline at end of file