Saltar al contenido principal
LibreTexts Español

2.5: Flujo de control condicional

  • Page ID
    55122
  • \( \newcommand{\vecs}[1]{\overset { \scriptstyle \rightharpoonup} {\mathbf{#1}} } \) \( \newcommand{\vecd}[1]{\overset{-\!-\!\rightharpoonup}{\vphantom{a}\smash {#1}}} \)\(\newcommand{\id}{\mathrm{id}}\) \( \newcommand{\Span}{\mathrm{span}}\) \( \newcommand{\kernel}{\mathrm{null}\,}\) \( \newcommand{\range}{\mathrm{range}\,}\) \( \newcommand{\RealPart}{\mathrm{Re}}\) \( \newcommand{\ImaginaryPart}{\mathrm{Im}}\) \( \newcommand{\Argument}{\mathrm{Arg}}\) \( \newcommand{\norm}[1]{\| #1 \|}\) \( \newcommand{\inner}[2]{\langle #1, #2 \rangle}\) \( \newcommand{\Span}{\mathrm{span}}\) \(\newcommand{\id}{\mathrm{id}}\) \( \newcommand{\Span}{\mathrm{span}}\) \( \newcommand{\kernel}{\mathrm{null}\,}\) \( \newcommand{\range}{\mathrm{range}\,}\) \( \newcommand{\RealPart}{\mathrm{Re}}\) \( \newcommand{\ImaginaryPart}{\mathrm{Im}}\) \( \newcommand{\Argument}{\mathrm{Arg}}\) \( \newcommand{\norm}[1]{\| #1 \|}\) \( \newcommand{\inner}[2]{\langle #1, #2 \rangle}\) \( \newcommand{\Span}{\mathrm{span}}\)\(\newcommand{\AA}{\unicode[.8,0]{x212B}}\)

    La frase “flujo de control” se refiere al hecho de que construcciones como bucles for cambian el flujo de ejecución del programa lejos del orden simple de arriba a abajo. Hay varios otros tipos de flujo de control que cubriremos, dos de los cuales son de naturaleza “condicional”.

    Uso de sentencias If

    Las sentencias IF nos permiten ejecutar condicionalmente un bloque de código, dependiendo de una variable que haga referencia a un Boolean True o False, o más comúnmente una condición que devuelve un Booleano True o False. La sintaxis es bastante simple, se describe aquí con un ejemplo.

    II.5_1_py_43_if_ejemplo

    Todas las líneas desde el if inicial hasta la última línea en un bloque elif: o bien: son parte de la misma construcción lógica. Tal construcción debe tener exactamente uno si es un bloque condicional, puede tener uno o más bloques elif (son opcionales), y puede tener exactamente un bloque catchall else al final (también opcional). Cada condicional se evalúa en orden: se ejecutará el primero que evalúe a True, y el resto se omitirá. Si hay un bloque else presente, se ejecutará si ninguno de los bloques if o elif anteriores lo hizo como “último recurso”.

    Al igual que con los bucles for, las sentencias if se pueden anidar dentro de otros bloques, y otros bloques pueden ocurrir dentro de los bloques de instrucciones if. También al igual que for-loops, Python usa indentación (la práctica estándar es de cuatro espacios por nivel de sangría) para indicar la estructura de bloques, por lo que obtendrá un error si aplica sangría innecesariamente (sin una línea de flujo de control correspondiente como for, if, elif, or else) u olvidarse de sangría cuando se espera una sangría. [1]

    II.5_2_PY_44_IF_Ejemplo2

    El código anterior imprimiría Número corto: 2 número largo: 2.

    Uso de While-Loops

    Los loops while se utilizan con menos frecuencia (dependiendo de la naturaleza de la programación que se esté realizando), pero pueden ser invaluables en ciertas situaciones y son una parte básica de la mayoría de los lenguajes de programación. Un bucle while ejecuta un bloque de código siempre que una condición permanezca True. Tenga en cuenta que si la condición nunca se convierte en False, el bloque se ejecutará una y otra vez en un “bucle infinito”. Sin embargo, si la condición es False para empezar, el bloque se omite por completo.

    II.5_3_PY_44_2_While_Pre_Ejemplo

    Lo anterior imprimirá Contador es ahora: 0, seguido de Contador es ahora: 1, Contador es ahora: 2, Contador es ahora: 3, y finalmente Hecho. El mostrador termina con: 4. Al igual que con el uso de un bucle for sobre un rango de enteros, también podemos usar un bucle while para acceder a índices específicos dentro de una cadena o lista.

    II.5_4_py_45_mientras_ejemplo1

    El código anterior se imprimirá base es: A, entonces base es: C, y así sucesivamente, terminando con base es: T antes de finalmente imprimir Hecho. Por lo tanto, los bucles while se pueden usar como un tipo de bucle for de grano fino, para iterar sobre elementos de una cadena (o lista), a su vez usando índices enteros simples y sintaxis []. Si bien el ejemplo anterior agrega 1 a base_index en cada iteración, podría agregar fácilmente algún otro número. Agregar 3 provocaría que se imprima cada tercera base, por ejemplo.

    Operadores Booleanos y Conectivos

    Ya hemos visto un tipo de comparación booleana, <, que devuelve si el valor de su lado izquierdo es menor que el valor de su lado derecho. Hay una serie de otros:

    Operador Significado Ejemplo (con a = 7, b = 3)
    < menos de? a < b # Falso
    > mayor que? a > b # Verdadero
    <= menor o igual a? a <= b # Falso
    >= mayor o igual a? a >= b # Verdadero
    ! = no es igual a? ¡a! = b # Verdadero
    == igual a? a == b # Falso

    Estas comparaciones funcionan para flotadores, enteros e incluso cadenas y listas. La clasificación en cadenas y listas se realiza en orden lexicográfico: un orden en el que el elemento A es menor que el elemento B si el primer elemento de A es menor que el primer elemento de B; en el caso de un empate, se considera el segundo elemento, y así sucesivamente. Si en este proceso nos quedamos sin elementos para la comparación, ese más corto es más pequeño. Cuando se aplica a cadenas, el orden lexicográfico corresponde al orden alfabético familiar.

    Imprimimos la versión ordenada de una lista de cadenas de Python, que hace su clasificación usando las comparaciones anteriores. Tenga en cuenta que los dígitos numéricos se consideran caracteres alfabéticos “menores que”, y las letras mayúsculas vienen antes que las letras minúsculas.

    II.5_5_py_46_sort_lexicográfico

    Los conectivos booleanos nos permiten combinar condicionales que devuelven True o False en declaraciones más complejas que también devuelven tipos booleanos.

    Conectivo Significado Ejemplo (con a = 7, b = 3)
    y Verdadero si ambos son True a < 8 y b == 3 # Verdadero
    o Verdadero si uno o ambos son True a < 8 o b == 9 # Verdadero
    no True si lo siguiente es False no a < 3 # Verdadero

    Estos pueden agruparse con paréntesis, y por lo general deben ser para evitar confusiones, sobre todo cuando más de una prueba siguen un no. [2]

    imagen

    Por último, tenga en cuenta que generalmente cada lado de un y o o debe resultar en solo Verdadero o Falso. La expresión a == 3 o a == 7 tiene la forma correcta, mientras que a == 3 o 7 no. (De hecho, 7 en este último contexto se tomará como Verdadero, y así un == 3 o 7 siempre resultará en Verdadero.)

    Peligros lógicos

    Observe la similitud entre = y ==, y sin embargo tienen significados dramáticamente diferentes: el primero es el operador de asignación de variables, mientras que el segundo es una prueba de igualdad. Usar accidentalmente uno donde se entiende el otro es una manera fácil de producir código erróneo. Aquí count == 1 no inicializará count a 1; más bien, devolverá si ya es 1 (o resultará en un error si count no existe como variable en ese punto). El error inverso es más difícil de cometer, ya que Python no permite la asignación de variables en definiciones de sentencia if y while loop.

    II.5_7_PY_47_IF_Seguridad

    En lo anterior, la intención es determinar si la longitud de seq es un múltiplo de 3 (según lo determinado por el resultado de len (seq) %3 usando el operador de módulo), pero la sentencia if en este caso debería ser realmente si resto == 0:. En muchos idiomas, lo anterior sería un error difícil de encontrar (el resto se asignaría a 0, ¡y el resultado sería True de todos modos!). En Python, el resultado es un error: SyntaxError: sintaxis no válida.

    Aún así, una cierta clase de comparación peligrosa es común a casi todos los idiomas, incluido Python: la comparación de dos tipos flotantes para igualdad o desigualdad.

    Aunque los enteros se pueden representar exactamente en aritmética binaria (por ejemplo, 751 en binario se representa exactamente como 1011101111), los números de punto flotante solo se pueden representar aproximadamente. Este no debería ser un concepto completamente desconocido; por ejemplo, podríamos decidir redondear fracciones a cuatro decimales al hacer cálculos en lápiz y papel, trabajando con 1/3 como 0.3333. El problema es que estos errores de redondeo pueden agravarse de maneras difíciles de predecir. Si decidimos computar (1/3) * (1/3)/(1/3) como 0.3333*0.3333/0.3333, trabajando de izquierda a derecha comenzaríamos con 0.3333*0.3333 redondeado a cuatro dígitos como 0.1110. Esto se divide luego por 0.3333 y se redondea nuevamente para producir una respuesta de 0.3330. Entonces, aunque sabemos que (1/3) * (1/3)/(1/3) == 1/3, nuestro proceso de cálculo los llamaría desiguales porque finalmente prueba 0.3330 contra 0.3333!

    Las computadoras modernas tienen muchos más dígitos de precisión (alrededor de 15 dígitos decimales como mínimo, en la mayoría de los casos), pero el problema sigue siendo el mismo. Peor aún, los números que no necesitan redondeo en nuestro sistema aritmético Base-10 sí requieren redondear en el sistema Base-2 de la computadora. Considere 0.2, que en binario es 0.001100110011, y así sucesivamente. En efecto, 0.2 * 0.2/0.2 == 0.2 resultados en ¡Falso!

    Si bien la comparación de flotadores con <, >, <= y >= suele ser segura (dentro de márgenes de error extremadamente pequeños), la comparación de flotadores con == y! = suele indicar un malentendido de cómo funcionan los números de punto flotante. [3] En la práctica, determinaríamos si dos valores de punto flotante son suficientemente similares, dentro de algún margen de error definido.

    II.5_8_PY_48_Epsilon_Compare

    Codones de parada de conteo

    Como ejemplo del uso de flujo de control condicional, consideraremos el archivo seq.txt, que contiene una sola cadena de ADN en la primera línea. Deseamos contar el número de codones de parada potenciales “TAG”, “TAA” o “TGA” que ocurren en la secuencia (solo en la cadena directa, para este ejemplo).

    Nuestra estrategia será la siguiente: Primero, tendremos que abrir el archivo y leer la secuencia desde la primera línea. Tendremos que mantener un contador del número de codones de parada que veamos; este contador comenzará en cero y le agregaremos uno por cada subsecuencia de “TAG”, “TAA” o “TGA” que veamos. Para encontrar estas tres posibilidades, podemos usar un for-loop y string slicing para inspeccionar cada subsecuencia de 3bp de la secuencia; la secuencia de 3bp en el índice 0 de seq ocurre en seq [0:3], la de la posición 1 ocurre en seq [1:4], y así sucesivamente.

    Debemos tener cuidado de no intentar leer una subsecuencia que no ocurra en la secuencia. Si seq = “AGAGAT”, solo hay cuatro secuencias posibles de 3bp, e intentar seleccionar la que comienza en el índice 4, seq [4:7], resultaría en un error. Para empeorar las cosas, la indexación de cadenas comienza en 0, y también están las peculiaridades de la naturaleza inclusivo/exclusiva de [] slicing y la función range ()!

    Para ayudar, dibujemos una imagen de una secuencia de ejemplo, con varios índices y subsecuencias de 3bp que nos gustaría ver anotadas.

    II.5_9_SEQ_STO_Windows

    Dado un índice de inicio, la subsecuencia de 3bp se define como seq [index:index + 3]. Para la secuencia anterior, len (seq) devolvería 15. El primer índice de inicio que nos interesa es 0, mientras que el último índice de inicio que queremos incluir es 12, o len (seq) - 3. Si tuviéramos que usar la función range () para devolver una lista de secuencias de inicio que nos interesan, usaríamos range (0, len (seq) - 3 + 1), donde el + 1 da cuenta del hecho de que range () incluye el primer índice, pero es exclusivo en el último índice. [4]

    También debemos recordar ejecutar .strip () en la secuencia de lectura, ya que no queremos que la inclusión de ningún\ n caracteres de nueva línea ¡estropeando el cálculo correcto de la longitud de la secuencia!

    Observe en el siguiente código (que se puede encontrar en el archivo stop_count_seq.py) la línea comentada #print (codon).

    II.5_10_py_49_stop_count_ex

    Mientras codificamos, se utilizó esta línea para imprimir cada codón para estar seguros de que las subsecuencias de 3bp estaban siendo consideradas de manera confiable, especialmente la primera y la última en seq1.txt (ATA y AAT). Esta es una parte importante del proceso de depuración porque es fácil hacer pequeños errores “off-by-one” con este tipo de código. Cuando estamos satisfechos con la solución, simplemente comentamos la declaración impresa.

    Para tareas de ventana como esta, ocasionalmente puede ser más fácil acceder a los índices con un bucle while.

    II.5_11_py_49_2_stop_count_ex_while

    Si quisiéramos acceder a codones no superpuestos, podríamos usar index = index + 3 en lugar de index = index + 1 sin ningún otro cambio en el código. Del mismo modo, si quisiéramos inspeccionar ventanas de 5bp, podríamos reemplazar instancias de 3 por 5 (o usar una variable windowsize).

    Ejercicios

    1. El peso molecular de una cadena de ADN monocatenario (en g/mol) es (recuento de “A”) *313.21 + (recuento de “T”) *304.2 + (recuento de “C”) *289.18 + (recuento de “G”) *329.21 — 61.96 (para dar cuenta de la eliminación de un fosfato y la adición de un hidroxilo en el sencillo hebra).

      Escriba código que imprima el peso molecular total de la secuencia en el archivo seq.txt. El resultado debe ser 21483.8. Llama a tu programa mol_weight_seq.py.

    2. El archivo seqs.txt contiene una serie de secuencias, una secuencia por línea. Escribe un nuevo programa Python que imprima el peso molecular de cada uno en una nueva línea. Por ejemplo: Es posible
      ii-5_11b_py_out_ex que desee utilizar partes sustanciales de la respuesta para la pregunta 1 dentro de un bucle de algún tipo. Llama a tu programa mol_weight_seqs.py.
    3. El archivo ids_seqs.txt contiene las mismas secuencias que seqs.txt; sin embargo, este archivo también contiene ID de secuencia, con un ID por línea seguido de un carácter tabulador (\ t) seguido de la secuencia. Modifique su programa de la pregunta 2 para imprimir la misma salida, en un formato similar: un ID por línea, seguido de un carácter de tabulación, seguido del peso molecular. El formato de salida debería así verse así (pero los números serán diferentes, para evitar regalar la respuesta):
      II.5_12_py_49_2_mol_peso_fuera_ex Llama a tu programa mol_weight_ids_seqs.py.

      Debido a que los caracteres de tabulación hacen que la salida se alinee de manera diferente dependiendo de la longitud de la cadena ID, es posible que desee ejecutar la salida a través de la columna de herramienta de línea de comandos con una opción -t, que formatea automáticamente la entrada separada por tabulaciones
      II.5_13_PY_50_mol_peso3_salida

    4. Crear una versión modificada del programa en la pregunta 3 del capítulo 15, “Colecciones y Looping, Parte 1: Listas y para”, para que también identifique las ubicaciones de las subsecuencias que son autosuperpuestas. Por ejemplo, “GAGA" ocurre en “GAGAGAGAGAGATATGAGA" en las posiciones 1, 3, 5, 7 y 14.

    1. Python es uno de los únicos lenguajes que requieren que los bloques sean delineados por sangría. La mayoría de los otros lenguajes utilizan pares de llaves para delinear bloques. Muchos programadores sienten que esto elimina demasiada creatividad en el formateo del código Python, mientras que otros así impone un método común para distinguir visualmente los bloques.
    2. En ausencia de paréntesis para agrupar, y tiene prioridad sobre o, al igual que la multiplicación tiene prioridad sobre la suma en álgebra numérica. La lógica booleana, por cierto, lleva el nombre del matemático y filósofo del siglo XIX George Boole, quien primero describió formalmente el “álgebra lógica” de combinar los valores de la verdad con conectivos como “y”, “o” y “no”.
    3. Puedes ver esto por ti mismo: print (0.2*0.2/0.2 == 0.2) impresiones Falso! Algunos lenguajes matemáticamente orientados son capaces de trabajar completamente simbólicamente con tales ecuaciones, evitando en absoluto la necesidad de trabajar con números. Esto requiere un motor de análisis sofisticado pero permite que dichos lenguajes evalúen incluso expresiones genéricas como x*x/x == x como true.
    4. Sí, este tipo de pensamiento lógico detallado puede ser tedioso, pero se vuelve más fácil con la práctica. Dibujar imágenes y considerar pequeños ejemplos también es invaluable cuando se trabaja en problemas de programación, así que ten a mano un lápiz y un trozo de papel.

    This page titled 2.5: Flujo de control condicional is shared under a CC BY-NC-SA license and was authored, remixed, and/or curated by Shawn T. O’Neil (OSU Press) .