Estructuras de Control - IF()

Formas de realizar una pregunta: IF()

La sentencia IF() permite hacer una pregunta y tomar decisiones a partir de la respuesta obtenida a dicha interrogación. Esta posibilidad es extremadamente útil cuando se plantean cálculos a resolver dónde los resultados de ciertas operaciones indican y permiten la elección de las fórmulas y algoritmos matemáticos para la solución del problema. Por lo cual puedo hacer una pregunta sobre ciertas variables y a partir de esa respuesta elegir el camino que sigue el programa. Por ejemplo, en resolución de una ecuación de segundo grado si el discriminante (\(B^2-4AC\)) es menor que cero, la solución está en el campo de los números complejos y no en la de los reales, por lo cual se deben tomar previsiones para este caso. Otro ejemplo, mucho más complicado podría darse si se están calculando modelos de la estructura de una estrella, para cierta capa a una profundidad dada podría haber sólo transferencia de energía por radiación o por convección según la física local (presión, temperatura, etc). Con lo cual el programa podría evaluar esas variables y decidir la física a utilizar.

Hay 3 formas distintas de usar esta orden. La primera de ellas no la usaremos porque ya es obsoleta (conocida como forma aritmética), nos concentraremos en las dos formas modernas de usarla: Esto es: el IF() sentencia y en el IF() en bloque (block if en inglés).

IF() Sentencia

El IF() sentencia funciona haciendo una pregunta a una expresión o variable, cuyo resultado es una respuesta Booleana, es decir estamos en el caso que la respuesta a la interrogación es un Verdadero o un Falso. Se hace la pregunta y si la respuesta es verdadera se ejecuta la sentencia que está a la derecha en el mismo renglón que la pregunta. Si es falsa esa orden escrita a la derecha no se ejecuta y se continua con el programa. La sentencia tiene esta forma:
IF(algo) sentencia
donde “algo” en su forma más general es una expresión booleana cuya resolución nos da un resultado verdadero o falso. También podría ser una variable Lógica sola cuyo valor asignado en el programa sea un verdadero o un falso.

Si la pregunta tiene que ver con comparar números, se deberán usar los Operadores de Relación.

Operadores de Relación

Estos son: \(>\), \(\geq\), \(=\), \(\neq\), \(<\), \(\leq\) pero como esos símbolos matemáticos no existían en los teclados antiguos, Fortran y otros lenguajes tienen su propia manera de escribirlos, ver tabla 1.1.

Forma de escribir en Fortran los operadores relacionales. Note que se escriben con puntos al comienzo y al final del nombre. Esta notición con puntos permite al compilador no confundirlos con una variable que podría tener esos nombres.
Operador Escritura en Fortran
\(>\) .GT.
\(\geq\) .GE.
\(=\) .EQ.
\(\neq\) .NE.
\(<\) .LT.
\(\leq\) .LE.

Ejemplo del uso de IF() com estos operadores:
IF(A.GT.10) A = B**2 + C**2
en este caso estoy indicando que si \(A>10\), el valor de \(A\) cambiará por el cálculo de \(A=B^2+C^2\) de lo contrario seguirá con el valor que ya tenía asignado.
Es importante volver a notar que la repuesta a la pregunta del IF() es Booleana, por lo cual nada evita que la haga de esta manera:
Logical L \(\ \ \ \ \) ! Ahora L es una variable lógica. Esta sentencia tiene que estar al principio del programa, en la zona donde defino variables.
L= A.GT.10 \(\ \ \) ! Comparo A con 10 y si es cierto guardo en L un verdadero y si no los es un Falso
IF(L) A = B**2 + C**2 \(\ \ \ \ \) ! Pregunto que sobre L, ya que ahí se guardó el resultado del cálculo de la expresión anterior que tiene como resultado lógico un verdadero o un falso.

Operadores Lógicos

Para tener una mayor cantidad de opciones existen los Operadores Lógicos. Estos sirven para realizar varias preguntas simultáneas. Ya que permiten comparar distintos resultados lógicos con reglas que impongo a través de estos operadores. A estos operadores los conocen de la asignatura Álgebra I. Recordemos los de uso más común, ver tabla 1.2

Forma de escribir en Fortran los operadores relacionales. Note que se escriben con puntos al comienzo y al final del nombre, al igual que vimos en el caso de los operadores de relación.
Operador Fortran Operación
Negación .NOT. Cambia el valor de la expresión lógica a su opuesto
Conjunción .AND. Cierto únicamente si ambas expresiones lógicas son ciertas
Disyunción Inclusiva .OR. Cierto si una de las expresiones es cierta
Disyunción exclusiva .XOR. Cierto únicamente si una de las expresiones es cierta
Equivalente .EQV. Cierto si ambas expresiones tienen el mismo valor
No equivalente .NEQV. Cierto si ambas expresiones no tienen el mismo valor

Prioridades de las operaciones

El resultado Booleano que uno espera en una expresión lógica, como hemos visto, puede ser la combinación de una cantidad de operaciones matemáticas, de relación y por último lógicas. Por lo cual se puede hacer tabla con el orden de prioridades de cómo estas operaciones son resueltas. Veamos la tabla 1.3:

Prioridades de los operadores en la forma más general de una expresión aritmética/boolena posible. Estas prioridades pueden sobre escribirse poniendo los paréntesis adecuados
Tipo de Operación Operador Asociatividad Prioridad
Aritmética ** (potencia) Derecha a izquierda 1
*, / Izquierda a derecha 2
+,- Izquierda a derecha 3
Relacionales .GT., .GE., .EQ., .NE., .LT., .LE. No tienen 4
Lógicos .NOT. Derecha a izquierda 5
.AND. Izquierda a derecha 6
.OR. Izquierda a derecha 7
.EQV., .NEQV. Izquierda a derecha 8

Sentencia GOTO

El IF sentencia tiene una limitación muy importante y es que si la condición se cumple sólo puede ejecutar una sola orden y no más. Históricamente forma de programar se la ha combinado con una sentencia llamada GOTO y suele considerarse como una muy manera poco feliz de realizar programas, ya que los códigos escritos de esta forma son muy confusos y difíciles de modificar. La orden GOTO funciona de la siguiente manera:
\(\ \ \ \ \ \ \ \)GOTO 99

\(\ \ \ \ \ \ \ \ \vdots\) se saltean estas líneas

\(99 \ \ \ \ \ \)sigue el programa...
Cuando la ejecución de un programa encuentra la orden GOTO en vez de seguir con la sentencia que está debajo salta a la que tiene la etiqueta (99). Esta etiqueta puede ser una sentencia posterior o incluso anterior a la que está el GOTO.
Esta orden tiene en si una utilidad, por ejemplo, para romper un ciclo DO de la siguiente manera: si en un cálculo tengo programados un bucle DO muy largo y descubro que no necesito terminarlo, porque el resultado ya esta calculado (ejemplo: una serie convergió muy rápido) puedo preguntar si esto pasó y escapar del DO antes que termine y ahorrar un montón de tiempo de computación. Así:
\(\ \ \ \ \ \ \ \)DO i=1,100000

\(\ \ \ \ \ \ \ \) \(\vdots\)

\(\ \ \ \ \ \ \ \)IF (algo) GOTO 20

\(\ \ \ \ \ \ \ \) \(\vdots\)

\(\ \ \ \ \ \ \ \)ENDDO

20 \(\ \ \ \)CONTINUE

\(\ \ \ \)sigue el programa...
Ejemplo: cálculo la serie de Taylor de la función cos(x), con un error menor a \(error < 10^{-6}\)
\[Cos(x) = \sum_{i=1}^\infty \frac{ (-1)^{i}x^{2i}}{(2i)!} = 1 - \frac{x^2}{2!} + \frac{x^4}{4!}-\frac{x^6}{6!} + ... + \lvert error(\xi) \rvert\]
Veamos el programa:
El programa es simple, vamos calculando los términos de la serie y sumándolos, pero al mismo tiempo calculamos el término del error. Evaluando ese término sabemos si llegamos al valor del error que requerimos. Si no llegamos a satisfacer la condición de tener un error más pequeño del que establecimos como satisfactorio, calculamos un término más a la serie y continuamos el proceso. Pero si obtenemos un error que nos determina que ya calculamos un valor útil para nuestras necesidades, el IF() nos permite terminar el programa y no hacer más cálculos innecesarios consumiendo recursos de la computadora.
\(\ \ \ \ \ \ \ \) Program cos_de_angulo 1\(^{,}\)2

\(\ \ \ \ \ \ \ \)

\(\ \ \ \ \ \ \ \) write(*,*) ’ingrese el valor del ángulo n’

\(\ \ \ \ \ \ \ \) read(*,*) omega

\(\ \ \ \ \ \ \ \) pi=3.14159265358979

\(\ \ \ \ \ \ \ \) omega=omega/180*pi

\(\ \ \ \ \ \ \ \) xmax=pi/2

\(\ \ \ \ \ \ \ \)

\(\ \ \ \ \ \ \ \) coseno=0

\(\ \ \ \ \ \ \ \) do i=0,1000000

\(\ \ \ \ \ \ \ \ \ \)

C\(\ \ \ \ \ \ \ \) Calculo el factorial para el término y para del error: n! y (n+2)!

\(\ \ \ \ \ \ \ \ \ \) facto=1.

\(\ \ \ \ \ \ \ \ \ \) do ii=1,2*i

\(\ \ \ \ \ \ \ \ \ \ \ \) facto=facto*ii

\(\ \ \ \ \ \ \ \ \ \) enddo

\(\ \ \ \ \ \ \ \ \ \) facto2=facto*(i+1)*(i+2)

\(\ \ \ \ \ \ \ \ \ \)

C\(\ \ \ \ \ \ \ \) Calculo el termino i

\(\ \ \ \ \ \ \ \ \ \) term=omega**(2*i)*(-1)**(i)/facto

\(\ \ \ \ \ \ \ \)

C\(\ \ \ \ \ \ \ \) Calculo el término y el error

\(\ \ \ \ \ \ \ \ \ \) coseno=coseno+term

\(\ \ \ \ \ \ \ \ \ \) eterm= xmax**(2*i+2)/facto2

\(\ \ \ \ \ \ \ \)

C\(\ \ \ \ \ \ \ \)Pregunto si llegué al error deseado

\(\ \ \ \ \ \ \ \ \ \) if(eterm.LT.1E-8) goto 99

\(\ \ \ \ \ \ \ \) enddo

\(\ \ \ \ \ \ \ \)

99\(\ \ \ \ \ \ \ \) write(*,*) i, coseno, cos(omega)

\(\ \ \ \ \ \ \ \)

\(\ \ \ \ \ \ \ \) end

Bloque IF() (Block IF)

El IF utilizado como bloque permite subsanar la deficiencia que tiene el IF sentencia de que sólo permite la ejecución de una sola orden, con este comando es posible realizar una serie muy larga de órdenes . Funciona de esta manera:
IF (expresión lógica) THEN
sentencia 1
sentencia 2
...
ELSEIF(expresión lógica 2) THEN
sentencia
sentencia
...
ELSEIF(expresión lógica 3) THEN
sentencia
sentencia
....
ELSE
sentencia
sentencia
...
ENDIF
¿Cómo funciona esta estructura de órdenes?
La primera sentencia sólo se diferencia del caso anterior porque a su derecha está la palabra THEN que le indicaría al compilador que estamos en el caso del IF() en bloque y no en el caso anterior del IF() sentencia. Esta orden se podría traducir al lenguaje coloquial como: Si pasa esto entonces hacemos todo esto que sigue y pero si no pasó esto pero pasó esto otro hacemos todas estas otras cosas. Y en cada caso lo que se hace no es ejecutar una sola orden sino todo un conjunto de órdenes sin límite de cantidad. Pueden escribirse todas las preguntas diferentes que se necesiten para resolver el problema. Veámoslo con más detalle:
Viendo el esquema, si la primera expresión lógica es verdadera se cumplen la sentencia 1, luego la 2 y así hasta que estas se acaban. Si la pregunta dio como resultado un verdadero, todas las órdenes que siguen se saltean y luego el programa continua su ejecución después del ENDIF.
Si la expresión lógica es falsa. Se continua con las siguientes preguntas en orden. Si la expresión lógica de primer ELSEIF es cierta se cumplen con las sentencias asociadas a este ELSEIF() y luego el programa continua con las sentencias que siguen al ENDIF. Si la expresión lógica no es cierta, se pregunta si es cierta la del segundo ELSEIF() y así sucesivamente con todos los ELSEIF() que hayan. Si ninguna de las expresiones del IF() o de los ELSEIF() son verdaderas, y sólo si en ese caso, se activan las sentencias del ELSE final. Este actúa en la forma de que solo se cumplen las sentencias debajo de él en el caso de que ninguna pregunta que se haya hecho haya sido verdadera.
Salvo la orden inicial IF() THEN y la última ENDIF todas las demás sentencias internas pueden o no estar. Nada impide tener la cantidad de sentencias ELSEIF() THEN que se requieran para la tarea, o ninguna. Y en el caso del ELSE si lo lógica de lo que está programando no lo necesita esta orden no se la utiliza y por consiguiente no se la escribe. Básicamente el IF en bloque es una estructura de módulos de los cuales se eligen los que se requieran para la resolver la tarea.
Ejemplos:


  1. Atención: Este programa es muy ineficiente, ya que se repiten cálculos innecesariamente. Puede ser mejorado muy fácilmente, pero se volverá muy confuso y dejaría de tener utilidad con el fin de que sea un ejemplo. Se deja como ejercicio al lector el intentar mejorar su eficiencia, modificándolo para que realice la menor cantidad de cálculos posibles e igual arribe al resultado correcto.↩︎

  2. Note que hemos asumido que \(0^0=1\) cuando esta operación da como resultado un valor indeterminado. Muchos lenguajes de computación (FORTRAN, Python, etc) consideran que el resultado es \(0^0=1\) por razones de simetría. Aunque este criterio puede ser discutible y no está generalizado a todos los lenguajes.↩︎

  3. Recordar que en Fortran los números complejos se escriben como un par ordenado, con la siguiente estructura: (parte real, parte imaginaria)↩︎