Cap. 5 - Condicionales
Los programas se vuelven interesantes cuando chequeamos condiciones y modificamos el comportamiento del programa según el resultado. Ese es el tema central de este capítulo.
5.1) Valores y expresiones booleanos
Un valor booleano es o bien verdadero o bien falso. Se llama así en honor del matemático británico George Boole, quien fuera el primero en formular el álgebra booleana (reglas que permiten razonar con estos valores y combinarlos). Estas reglas son la base de toda la lógica computacional moderna.
En Python, los dos valores booleanos son True y False (deben escribirse exactamente así, con la "T" y "F" mayúscula y el resto en minúsculas). El tipo correspondiente se llama bool.
>>> type(True)>>> type(true) Traceback (most recent call last): File " ", line 1, in NameError: name 'true' is not defined
Una expresión booleana es una expresión al evaluarse produce un valor booleano. Por ejemplo, el operador == chequea si dos valores son iguales, y devuelve un booleano.
>>> 5 == (3 + 2) # ¿es cinco igual al resultado de 3 + 2? True >>> (5 == 5) + 2 # sumarle 2 a True - devuelve 3 (si se le suma a False, devuelve 2; o sea que True se considera un 1 y False se considera un 0 en operaciones numéricas) 3 >>> 5 == 6 False >>> j = "hel" >>> j + "lo" == "hello" True
En los casos en que la igualdad es correcta se retorna True, en caso contrario se retorna False.
El operador == es uno de los 6 operadores de comparación comunes que producen un resultado booleano. La lista completa es:
x == y # Produce True si ... x es igual a y x != y # ... x no es igual a y x > y # ... x es mayor que y (estrictamente) x < y # ... x es menor que y (estrictamente) x >= y # ... x es mayor o igual que y x <= y # ... x es menor o igual que y
Si bien las operaciones son conocidas de la matemática, los símbolos que utiliza Python son similares, pero no iguales, a los de aquélla. Evitar el error común de utilizar un sólo signo de igual (=) en vez del doble igual (==). Recordar que el = es un operador de asignación y que el == es un operador de comparación. Por otro lado, no existen operadores como =< o =>
Como los demás tipos que hemos visto, los valores booleanos pueden asignarse a variables, imprimirse, etc.
>>> edad = 18 >>> con_suficiente_edad_como_para_manejar = edad >= 17 >>> print(con_suficiente_edad_como_para_manejar) True >>> type(con_suficiente_edad_como_para_manejar) <class 'bool'>
Observar en la línea 2 cómo a la variable con_suficiente_edad_como_para_manejar se le asigna un booleano (True o False) al asignársele la comparación edad >= 17. En este caso se le asigna True. Si más adelante alguien cambia la edad a 15, la variable con_suficiente_edad_como_para_manejar seguirá siendo True (pues así había sido evaluada). Habría que volver a asignarle edad >= 17 para que ahora guarde un False.
5.2) Operadores lógicos
Hay 3 operadores lógicos and, or y not, que nos permiten construir expresiones booleanas más complejas a partir de otras simples. La semántica (significado) de esos operadores es el mismo que el habitual en inglés.
Ejemplos:
x > 0 and x < 10
devuelve True sólo si x es mayor que 0 y al mismo tiempo x es menor que 10.n % 2 == 0 or n % 3 == 0
devuelve True si cualquiera de las dos condiciones es verdadera, es decir, que el número n sea divisible por 2 o que el número n sea divisible por 3
- (si ambas condiciones se cumplen también devuelve True, ya que "or" requiere que al menos una de ellas sea verdadera)
- el operador not es una negación, por lo tanto
not (x > y)
es True si(x > y)
es False, es decir, si x es menor o igual que y
La expresión a la izquierda de un or se evalúa primero. Si da True, Python ya no evalúa la de la derecha (pues no es necesario hacerlo para saber que el resultado global es True). Esto se llama evaluación de cortocircuito (o evaluación de circuito corto). Del mismo modo, para el operador and, si la expresión de la izquierda da False, Python ya no evalúa la expresión de la derecha porque sabe que el global dará False.
Es decir que no se realizan evaluaciones innecesarias.
5.3) Tablas de verdad
Una tabla de verdad es una pequeña tabla que nos permite listar todas las posibles entradas y resultados de un operador lógico. Como los operadores and y or tienen ambos dos operandos, hay 4 filas en sus respectivas tablas de verdad.
Tabla de verdad para el operador and:
a b a and b False False False False True False True False False True True True
Para no escribir True y False cada vez, podemos abreviar utilizando T y F, o V y F en español.
Tabla de verdad para el operador or:
a b a or b F F F F T T T F T T T T
Como el operador or sólo tiene un operando, su tabla de verdad tendrá sólo dos filas:
a not a F T T F
5.4) Simplificando expresiones booleanas
Se llama álgebra a cualquier conjunto de reglas que permitan simplificar y reorganizar expresiones. Por ejemplo, todos conocemos las reglas del álgebra matemática, del tipo:
n * 0 == 0
Ahora veremos otra, que es el álgebra booleana, que da reglas para trabajar con valores booleanos.
Primero, reglas para el operador and:
x and False == False False and x == False y and x == x and y x and True == x True and x == True x and x == x
Estas son las reglas correspondientes para el operador or:
x or False == x False or x == x y or x == x or y x or True == True True or x == True x or x == x
Dos operadores not se cancelan mutuamente:
not (not x) == x
5.5) Ejecución condicional
Para escribir programas útiles, casi siempre necesitamos chequear condiciones y cambiar el comportamiento del programa según ellas. Los enunciados condicionales nos permiten hacer esto. El más sencillo es el enunciado if, por ejemplo: (L5_if_else.py)
if x % 2 == 0: print(x, " es par ") print("Sabías que 2 es el único número par que es primo?") else: print(x, " es impar ") print("Sabías que multiplicando dos números impares " + "se obtiene siempre un número impar?")
Observación: este programa es interesante, también, porque muestra algunas formas habituales de utilizar la función print.
La expresión booleana tras el if se llama condición. Si es verdadera, entonces todos los enunciados indentados que la siguen son ejecutados. De lo contrario, todos los enunciados indentados tras el else son ejecutados.
La sintaxis de un enunciado if se ve así:
if EXPRESIÓN_BOOLEANA: SENTENCIAS_1 # Ejecutadas si la expresión booleana evalúa a True else: SENTENCIAS_2 # Ejecutadas si la expresión booleana evalúa a False
Como con la definición de función del capítulo anterior, o la de otras sentencias compuestas como for, vista antes, el enunciado if tiene un header y un cuerpo. La línea header comienza con la palabra clave if, seguida de una expresión booleana y termina con dos puntos. Las sentencias indentadas que vienen a continuación se llaman bloque. El primer enunciado no indentado señala el final del bloque.
Cada uno de los enunciados dentro del primer bloque se ejecutan en orden si la expresión booleana evalúa a True. Si en cambio evalúa a False, todo el bloque inicial es saltado y se ejecutan en su lugar las sentencias que están en el bloque que sigue a la línea else.
No hay límite a la cantidad de sentencias que se pueden poner en cada bloque, pero tiene que haber al menos una sentencia en cada bloque. De vez en cuando es práctico poder tener un bloque vacío (sin sentencias), por ejemplo como un lugar "a rellenar más adelante" si estamos en el proceso de ir escribiendo el código. En tal caso se puede utilizar la sentencia pass, que no hace nada (sólo actúa como un placeholder).
if True: # Esto siempre es True pass # así que esto siempre se ejecuta, pero no hace nada else: pass
5.6) Omitiendo la cláusula ELSE
Otra forma del enunciado if es aquella en que la cláusula else se omite por completo. En este caso, cuando el enunciado evalúa a True se ejecutan las sentencias del bloque, y en caso contrario el flujo de ejecución continúa en la línea posterior al if (la primera línea no indentada posterior al bloque). (L5_if_only.py)
import math x = -3 if x < 0: print("El número negativo ", x, " no es válido aquí.") x = 49 print("Decidí usar el número 49 en su lugar.") print("La raíz cuadrada de ", x, " es ", math.sqrt(x))
En este caso, la función print que muestra la raíz cuadrada está después de la sentencia if (ya que es la primera no indentada después del bloque del if). El llamado a math.sqrt requiere haber importado previamente el módulo math.
Terminología Python: en la documentación de Python a veces se llama suite (sucesión) de sentencias a lo que aquí hemos llamado bloque. Significan lo mismo, pero como en otros lenguajes y en la teoría de la computación en general se suele utilizar el término bloque, lo preferimos antes que el de uso más restringido.
Es importante observar además que else no es una sentencia, sino que es una cláusula que es parte de la sentencia if (la cual puede o no contener a la cláusula else)
5.7) Condicionales encadenados
A veces hay más de dos opciones y necesitamos más ramas. Una forma de conseguirlo es mediante condicionales encadenados:
if x < y: SENTENCIAS_A elif x > y: SENTENCIAS_B else: SENTENCIAS_C
elif
es una abrevaición de else if
. Como antes, sólo una de las ramas será ejecutada (la primera cuya condición se cumpla). No hay límite a cuántos elif se pueden poner pero sólo se permite un else (opcional) final y debe ser la última cláusula de la sentencia.
if choice == "a": function_one() elif choice == "b": function_two() elif choice == "c": function_three() else: print("Invalid choice.")
Cada condición se chequea en orden. Si la primera es falsa, la próxima es chequeada, y así sucesivamente. Si una es verdadera, el correspondiente bloque se ejecuta, y la sentencia termina. Incluso si más de una condición es verdadera, sólo se ejecuta el bloque de la primera rama verdadera.
5.8) Condicionales anidados
Se puede anidar también a un condicional dentro de otro (viene a ser un caso más de composición). Podríamos haber escrito el ejemplo anterior así:
if x < y: STATEMENTS_A else: if x > y: STATEMENTS_B else: STATEMENTS_C
El condicional externo contiene 2 ramas. La segunda rama contiene otra sentencia if, que tiene sus propias dos ramas. Esas dos ramas podrían a su vez haber contenido más condicionales.
A pesar de que la indentación de los condicionales facilita la comprensión, muchos condicionales anidados son difíciles de leer. En general es buena idea evitarlos tanto como sea posible.
Los operadores lógicos proveen habitualmente una forma de simplificar sentencias condicionales. Por ejemplo, podemos reescribir el siguiente código con un único condicional:
if 0 < x: # Suponemos aquí que x es un entero if x < 10: print("x es un entero positivo y tiene un único dígito.")
La función print sólo se llama si dieron True las dos condiciones. Por lo tanto, podríamos utilizar el operador and para lograr exactamente el mismo efecto, con un solo condicional:
if 0 < x and x < 10: # Suponemos aquí que x es un entero print("x es un entero positivo y tiene un único dígito.")
5.9) La sentencia RETURN
La sentencia return (con o sin valor, según que la función sea o no fructífera) permite interrumpir la ejecución de una función antes de que llegue al final.
Una razón típica para un retorno temprano es haber detectado una condición de error. (L5_if_return.py)
def mostrar_raiz_cuadrada(x): if x < 0: print("Sólo números positivos, por favor") return raiz = x**0.5 print("La raíz cuadrada de ", x, " es ", raiz)
La función mostrar_raiz_cuadrada tiene un parámetro x. Lo primero que hace es chequear si x es negativo, en cuyo caso muestra un mensaje de error y la función retorna. El flujo de ejecución vuelve de inmediato al caller, y el resto de la función no se ejecuta.
5.10) Opuestos lógicos
Cada uno de los 6 operadores relacionales tiene un opuesto lógico. Por ejemplo, si podemos obtener la licencia de conducir a una edad mayor o igual a 17, quiere decir que no la podemos obtener a una edad menor que 17. Es decir, el opuesto de >= es <
operador opuesto lógico == != != == < >= <= > > <= >= <
Comprender bien estos opuestos lógicos nos puede ahorrar el uso de varios not. Los operadores not suelen ser difíciles de leer cuando el código se complica, y será más fácil de comprender qué intentábamos hacer si evitamos usarlos.
Por ejemplo, podemos escribir esto en Python:
if not (edad >= 17): print("Eres demasiado joven para tener una licencia!")
Pero es más claro utilizar la siguiente simplificación:
if edad < 17: print("Eres demasiado joven para tener una licencia!")
Dos leyes de simplificación poderosas, conocidas como leyes de de Morgan son útiles cuando trabajamos con expresiones lógicas complicadas:
not (x and y) == (not x) or (not y) not (x or y) == (not x) and (not y)
Por ejemplo, supongamos que sólo se puede matar al dragón si nuestra espada mágica está cargada con 90% o más de poder, y tenemos 100 o más unidades de poder en nuestro escudo protector. Encontraríamos un fragmento de código así en el programa correspondiente:
if not ((sword_charge >= 0.90) and (shield_energy >= 100)): print("Tu ataque no tiene efecto, el dragón te rostizó!") else: print("El dragón se hunde en el abismo. Has rescatado a la bella princesa!")
Las leyes de de Morgan junto con el uso de opuestos lógicos nos permiten escribir la condición de un modo más comprensible, así:
if (sword_charge < 0.90) or (shield_energy < 100): print("Tu ataque no tiene efecto, el dragón te rostizó!") else: print("El dragón se hunde en el abismo. Has rescatado a la bella princesa!")
También podríamos haber eliminado el not de la primera versión invirtiendo el orden de los bloques if y else, así:
if (sword_charge >= 0.90) and (shield_energy >= 100): print("El dragón se hunde en el abismo. Has rescatado a la bella princesa!") else: print("Tu ataque no tiene efecto, el dragón te rostizó!")
Tal vez esta última versión sea la mejor de las tres, porque es la que más se parece a como la diríamos hablando en lenguaje humano (en español o inglés). La claridad de nuestro código (para otros humanos) en el sentido de facilitar la comprensión de qué es lo que hace el código debería ser siempre de gran prioridad.
A medida que desarrollemos nuestras habilidades de programación comenzaremos a ver que hay varias formas de resolver el mismo problema. Por lo tanto, los buenos programas comienzan por un buen diseño. Llevamos a cabo elecciones que favorecen (o no) una mayor claridad, simplicidad y elegancia. El título arquitecto de software dice muchísimo sobre lo que hacemos: somos arquitectos que desarrollan sus productos buscando un balance entre belleza, funcionalidad, simplicidad y claridad en nuestras creaciones.
Tip: Cuando un programa ya funciona, deberíamos probarlo e irlo puliendo. Escribir buenos comentarios. Pensar dónde podría quedar más claro usando otra clase de nombres de variables y funciones. ¿Se podría hacer de un modo más sencillo? ¿Deberíamos usar más funciones? ¿Podemos simplificar los condicionales? Vemos nuestro código como una creación y una obra de arte, y buscamos mejorarlo en cada detalle.
5.11) Conversión de tipos
Ya hablamos de esto en algún capítulo anterior. Muchos tipos de Python vienen con funciones built-in que permiten convertir sus valores a otros tipos. La función int, por ejemplo, toma cualquier valor y lo convierte a un entero, si es posible, o bien emite un mensaje de error explicando que no es posible:
>>> int("32") 32 >>> int("Hello") ValueError: invalid literal for int() with base 10: 'Hello'
También int permite convertir números float (decimales) a enteros, pero recordemos que lo que hace es truncar la parte decimal:
>>> int(-2.3) -2 >>> int(3.99999) 3 >>> int("42") 42 >>> int(1.0) 1
La función float convierte enteros y strings a floats:
>>> float(32) 32.0 >>> float("3.14159") 3.14159 >>> float(1) 1.0 >>> float("hola") Traceback (most recent call last): File "<interactive input>", line 1, in <module> ValueError: could not convert string to float: 'hola'
Podría parecer extraño que Python haga distinción entre el valor 1 entero y el valor 1.0 float. Es cierto que representan el mismo número, pero pertenecen a distintos tipos. Es importante no confundirlos porque su representación interna es diferente (a nivel de la computadora).
La función str convierte a cualquier argumento al tipo string:
>>> str(32) '32' >>> str(3.14149) '3.14149' >>> str(True) 'True' >>> str(true) Traceback (most recent call last): File "<interactive input>", line 1, inNameError: name 'true' is not defined
Esta función convierte cualquier valor a un string. Como vimos antes, True es un valor booleano, pero true es un nombre de variable ordinario, y como no ha sido definido en un paso previo, devuelve un mensaje de error.
5.12) Un diagrama de barras con tortugas
Con la tortuga se pueden hacer muchas más cosas que las que vimos hasta ahora. La documentación completa sobre el módulo turtle se puede encontrar en http://docs.python.org/py3k/library/turtle.html, o en PyScripter, buscando "turtle" en la Ayuda.
Aquí tenemos un par de trucos nuevos para la tortuga:
- Podemos hacer que la tortuga escriba texto en el canvas (en su posición actual). El método que permite hacer esto es:
alex.write("Hola")
- Podemos rellenar una figura (círculo, semicírculo, triángulo, etc.) con un color. Funciona en dos etapas:
- Primero se llama aalex.begin_fill()
, luego se dibuja la figura, y por último se llama aalex.end_fill()
- Así como podemos setear el color de la tortuga (de su trazo), también podemos setear el color de relleno, que no tiene por qué ser el mismo
- Usamosalex.color("blue", "red")
para indicar a la tortuga que debe dibujar en azul y rellenar en rojo
Con todo lo anterior, hagamos que tess dibuje un diagrama de barras. Comencemos con una lista de valores a graficar:
xs = [48, 117, 200, 240, 160, 260, 220]
Dibujaremos rectángulos cuyas alturas correspondan a los datos anteriores y cuyos anchos sean del mismo valor: (L5_bar_chart.py)
import turtle def dibujar_barra(t, alto): """ Hacer que la tortuga t dibuje un rectángulo de altura alto.""" t.left(90) t.forward(alto) # Dibuja el lado izquierdo del rectángulo t.right(90) t.forward(40) # Dibuja el ancho por el lado de arriba t.right(90) t.forward(alto) # Dibuja el lado derecho t.left(90) # Hacer que la tortuga mire para el lugar hacia donde miraba inicialmente t.forward(10) # Dejar una pequeña distancia entre barra y barra wn = turtle.Screen() #Setear la ventana wn.bgcolor("lightgreen") tess = turtle.Turtle() #Crear a tess y setear algunos atributos tess.color("blue") tess.pensize(3) tess.left(180) # Moverse hacia la izquierda antes de empezar a dibujar tess.forward(150) tess.left(180) xs = [48, 117, 200, 240, 160, 260, 220] for v in xs: dibujar_barra(tess, v) wn.mainloop()
Se obtiene algo así:
No muy impresionante, pero es un buen comienzo. Lo importante aquí fue que pudimos subdividir el problema en etapas: en cada etapa dibujamos una barra, y lo hacemos a través de una función que escribimos específicamente para conseguir esto. Luego, para dibujar todo el diagrama, llamamos varias veces (7 veces) a esta función.
Ahora, en el top de cada barra queremos imprimir el valor numérico correspondiente al dato de la misma.
Lo haremos mediante una línea t.write(' ' + str(alto))
justo antes de dibujar el "techo" de la barra, en la función dibujar_barra. Dejamos un pequeño espacio adelante del número para que se vea centrado. También convertimos el número en un string mediante la función str. El resultado se ve más o menos así:
Lo siguiente es que queremos rellenar las barras con un color, para lo cual modificamos la línea que setea el color, para que sea así: tess.color("blue", "red"). Y además agregamos llamados a t.begin_fill() y t.end_fill() en las líneas adecuadas del código de la función, obteniendo este código final: (L5_bar_chart_relleno.py)
import turtle def dibujar_barra(t, alto): """ Hacer que la tortuga t dibuje un rectángulo de altura alto.""" t.begin_fill() # Esta línea indica que vamos a rellenar t.left(90) t.forward(alto) # Dibuja el lado izquierdo del rectángulo t.write(' ' + str(alto)) t.right(90) t.forward(40) # Dibuja el ancho por el lado de arriba t.right(90) t.forward(alto) # Dibuja el lado derecho t.left(90) # Hacer que la tortuga mire para el lugar hacia donde miraba inicialmente t.end_fill() #Esta línea indica que rellenamos hasta acá t.forward(10) # Dejar una pequeña distancia entre barra y barra wn = turtle.Screen() #Setear la ventana wn.bgcolor("lightgreen") tess = turtle.Turtle() #Crear a tess y setear algunos atributos tess.color("blue", "red") tess.pensize(3) tess.left(180) # Moverse hacia la izquierda antes de empezar a dibujar tess.forward(150) tess.left(180) xs = [48, 117, 200, 240, 160, 260, 220] for v in xs: dibujar_barra(tess, v) wn.mainloop()
Lo que produce el siguiente gráfico:
Mejoró mucho. Todavía se podría mejorar eliminando la línea inicial y la conexión horizontal entre las barras en el piso. Esto se haría levantando el lápiz entre barra y barra, y lo dejamos como ejercicio.
5.13) Glosario
- bloque, cuerpo, álgebra booleana, expresión booleana, valor booleano (True o False), rama (uno de los posibles caminos de ejecución determinados por la ejecución de un condicional)
- condicional encadenado, operador de comparación, condición, enunciado condicional, operador lógico, anidación, prompt (un detalle visual que le indica al usuario que puede ingresar datos)
- tabla de verdad, conversión de tipos, envolver código en una función (proceso por el cual elegimos ciertas líneas de código para crear con ellas una nueva función)
5.14) Ejercicios
1) Suponer que los días de la semana están numerados 1, 2, 3, 4, 5, 6, 7 de Domingo a Sábado. Escribir una función que dado el número del día, devuelva su nombre (un string). (hacerlo)
2) Te vas de vacaciones un día 3 (miércoles). Volvés a casa después de dormir 137 noches. Escribir una función general que pida al usuario cuál es el día inicial y el largo de la estadía, y devuelva el nombre del día de la semana en que estarás volviendo. (hacerlo)
3) Dar los opuestos lógicos de estas condiciones
1) a > b 2) a >= b 3) a >= 18 and day == 3 4) a >= 18 and day != 3
- Solución:
1) a <= b 2) a < b 3) a < 18 or day != 3 4) a < 18 or day == 3
4) A qué evalúan estas expresiones?
1) 3 == 3 2) 3 != 3 3) 3 >= 4 4) not (3 < 4)
- Solución:
1) True 2) False 3) False 4) False
5) Completar esta tabla de verdad:
p q r (not(p and q)) or r F F F F F T F T F F T T T F F T F T T T F T T T
- Solución:
p q r (not(p and q)) or r F F F T F F T T F T F T F T T T T F F T T F T T T T F F T T T T
6) Escribir una función que recibe un puntaje de examen y devuelve un string (la calificación conceptual correspondiente a dicho puntaje), según el siguiente esquema: (hacerlo)
Nota Calificación >= 75 Primero [70-75) Segundo Superior [60-70) Segundo [50-60) Tercero [45-50) F1 Superior [40-45) F2 < 40 F3
- Los paréntesis recto y curvo denotan intervalos cerrados y abiertos respectivamente. Un intervalo cerrado incluye al número, y uno abierto lo excluye. Así, 39.999 obtiene un F3, pero 40 obtiene un F2.
- Aplicar los siguientes datos:
xs = [83, 75, 74.9, 70, 69.9, 65, 60, 59.9, 55, 50, 49.9, 45, 44.9, 40, 39.9, 2, 0]
- Testear el programa imprimiendo el puntaje y la calificación para todos los elementos de la lista.
7) Modificar el programa del diagrama de barras con la tortuga para que las líneas horizontales entre barra y barra ya no se dibujen. (hacerlo)
8) Modificar el programa del diagrama de barras para que los valores por encima de 200 se pinten con rojo, los que están entre 100 y 200 con amarillo, y los menores de 100 con verde. (hacerlo)
9) En el programa del diagrama de barras, ¿qué pasaría si alguno de los valores de la lista fuera negativo? Cambiar el programa para que el texto se imprima fuera del rectángulo en ese caso. (hacerlo)
- Respuesta: lo que pasa es que se dibuja el rectángulo hacia abajo - el único problema es que el número queda ahora adentro de la barra, razón por la que se pide que se encuentre el modo de corregir esto.
10) Escribir una función encontrar_hipotenusa que, dados los largos de los dos catetos de un triángulo rectángulo, retorna el largo de la hipotenusa. (hacerlo)
- Pista: para obtener la raíz cuadrada de x se puede utilizar x ** 0.5
11) Escribir una función es_rectángulo que dado el largo de 3 lados de un triángulo determina si el triángulo es o no es rectángulo. Asumir que el tercer parámetro corresponde siempre al lado más largo.
- Debe retornar
True
si el triángulo es rectángulo yFalse
en caso contrario (hacerlo)
- Pista: la aritmética de punto flotante no siempre es exacta, por lo cual no es muy seguro testear si dos números float son "iguales"
- Si un buen programador quiere saber si x es igual o muy cercano a y ("casi igual"), utilizará un código similar a este:
if abs(x - y) < 0.000001: # Si x es aproximadamente igual a y
12) Extender el programa anterior para que los 3 lados se puedan recibir en cualquier orden (ya no es necesario que el más largo sea el tercero) (hacerlo)
13) La razón por la que la aritmética float es inexacta se comprende con facilidad si uno intenta expresar 1/3 en números decimales.
- La representación de un número en la memoria de la computadora o en una calculadora de bolsillo tiene problemas similares: la memoria es finita y es necesario descartar algunos digitos
- Algunas pequeñas inexactitudes tienen efectos catastróficos, como se puede testear con este pequeño script: (crear un archivito con el script)
import math a = math.sqrt(2.0) print(a, a*a) print(a*a == 2.0)
El resultado da False
porque tanto math.sqrt(2.0)
como 2.0**0.5
devuelven un a
que no es exactamente igual a raíz(2), por lo cual al hacer a*a
surge una discrepancia como esta: 2.0000000000000004