[10. Arreglos ]  [Tutorial de Fortran]  [12. Entrada/salida Básica ]

11. Subprogramas

Cuando un programa tiene más de cien líneas, es difícil de seguir. Los códigos de Fortran que resuelven problemas reales de ingeniería por lo general tienen decenas de miles de líneas. La única forma para manejar códigos tan grandes, es usar una aproximación modular y dividir el programa en muchas unidades independientes pequeñas llamadas subprogramas.

Un subprograma es una pequeña pieza de código que resuelve un subproblema bien definido. En un programa grande, se tiene con frecuencia que resolver el mismo subproblema con diferentes tipos de datos. En vez de replicar el código, estas tareas pueden resolverse con subprogramas. El mismo subprograma puede ser llamado varias veces con distintas entradas de datos.

En Fortran se tienen dos tipos diferentes de subprogramas, conocidas como funciones y subrutinas.

Funciones

Las funciones en Fortran son bastante similares a las funciones matemáticas: ambas toman un conjunto de variables de entrada (parámetros) y regresan un valor de algún tipo. Al inicio de la sección se comento de los subprogramas definidas por el usuario, pero Fortran 77 tiene también funciones incorporadas.

Un ejemplo simple muestra como usar una función:

      x = cos(pi/3.0)
En este caso la función coseno cos de 60º, asignará a la variable x el valor de 0.5 (si pi ha sido definido correctamente; Fortran 77 no tiene constantes incorporadas). Hay varias funciones incorporadas en Fortran 77. Algunas de las más comunes son:
      abs     valor absoluto
      min     valor mínimo
      max     valor máximo
      sqrt    raíz cuadrada
      sin     seno
      cos     coseno
      tan     tangente
      atan    arco tangente
      exp     exponencial (natural)
      log     logaritmo (natural)
En general, una función siempre tiene un tipo. Varias de las funciones incorporadas mencionadas anteriormente son sin embargo genéricas. Por lo tanto en el ejemplo anterior pi y x podrían ser del tipo real o del tipo double precision. El compilador revisará los tipos y usará la versión correcto de la función cos (real o double precision). Desafortunadamente, Fortran no es un lenguaje polimórfico, por lo que en general, el programador debe hacer coincidir los tipos de las variables y las funciones.

Se revisa a continuación como implementar las funciones escritas por el usuario. Supongamos el siguiente problema: un meteorólogo ha estudiado los niveles de precipitación en el área de una bahía y ha obtenido un modelo (función) ll(m,t) donde ll es la cantidad de lluvia, m es el mes, y t es un parámetro escalar que depende de la localidad. Dada la fórmula para ll y el valor de t, calcular la precipitación anual

La forma obvia de resolver el problema es escribir un ciclo que corra sobre todos los meses y sume los valores de ll. Como el cálculo del valor de ll es un subproblema independiente, es conveniente implementarlo como una función. El siguiente programa principal puede ser usado:

      program lluvia
      real r, t, suma
      integer m
 
      read (*,*) t
      suma = 0.0
      do m = 1, 12
         suma = suma + ll(m, t)
      end do
      write (*,*) 'La precipitación Anual es ', suma, 'pulgadas'

      stop
      end
Además, la función ll tiene que ser definida como una función de Fortran. La fórmula del meteorólogo es:
      ll(m,t) = t/10 * (m**2 + 14*m + 46) si la expresión es positiva
      ll(m,t) = 0                         otro caso               
La correspondiente función en Fortran es
      real function ll(m,t)
         integer m
         real t

         ll = 0.1*t * (m**2 + 14*m + 46)
         if (ll .LT. 0) ll = 0.0

         return
      end
Se puede observar que la estructura de una función es parecida a la del programa principal. Las diferencias son:

Para resumir, la sintaxis general de una función en Fortran 77 es:

      tipo function nombre (lista_de parámetros)
         declaraciones
         sentencias
         return
      end

La función es llamada simplemente usando el nombre de la función y haciendo una lista de argumentos entre paréntesis.

Subrutinas

Una función de Fortran puede devolver únicamente un valor. En ocasiones se desean regresar dos o más valores y en ocasiones ninguno. Para este propósito se usa la construcción subrutina. La sintaxis es la siguiente:
      subroutine nombre (lista_de_parámetros)
         declaraciones
         sentencias
         return
      end
Observar que las subrutinas no tienen tipo y por consecuencia no pueden hacerse asignación al momento de llamar al procedimiento.

Se da un ejemplo de una subrutina muy sencilla. El propósito de la subrutina es intercambiar dos valores enteros.

      subroutine iswap (a, b)
         integer a, b
c Variables Locales
         integer tmp

         tmp = a
         a = b
         b = tmp

         return
      end
Se debe observar que hay dos bloques de declaración de variables en el código. Primero, se declaran los parámetros de entrada/salida, es decir, las variables que son comunes al que llama y al que recibe la llamada. Después, se declaran las variables locales, esto es, las variables que serán sólo conocidas dentro del subprograma. Se pueden usar los mismos nombres de variables en diferentes subprogramas.

Llamada por referencia

Fortran 77 usa el paradigma de llamada por referencia. Esto significa que en vez de pasar los valores de los argumentos a la función o la subrutina (llamada por valor), se pasa la dirección (apuntadores) de los argumentos.
      program llamaint
         integer m, n
c
         m = 1
         n = 2 

         call iswap(m, n)
         write(*,*) m, n

         stop
      end
La salida de este programa es "2 1", justo como se habría esperado. Sin embargo, si Fortran 77 hubiera hecho una llamada por valor entonces la salida hubiera sido "1 2", es decir, las variables m y n hubieran permanecido sin cambio. La razón de esto último, es que solamente los valores de m y n habrían sido copiados a la subrutina iswap, a pesar de que a y b hubieran sido cambiados dentro de la subrutina, por lo que los nuevos valores no sería regresados al programa que hizo la llamada.

En el ejemplo anterior, la llamada por referencia era lo que se quería hacer. Se debe tener cuidado al escribir código en Fortran, porque es fácil introducir efectos laterales no deseados. Por ejemplo, en ocasiones es tentador usar un parámetro de entrada en un subprograma como una variable local y cambiar su valor. No se deberá hacer nunca, ya que el nuevo valor se propagará con un valor no esperado.

Se revisará más conceptos cuando se vea la sección de Arreglos en subprogramas para pasar arreglos como argumentos.


Ejercicios

Ejercicios A
Escribir una función llamada fac que tome un entero n como entrada y regrese n! (factorial de n). Probar la función usando el siguiente programa main
      program prbfac
c
c Ejercicio A, seccion 11.
c Programa Main para probar la función factorial.
c
      integer n, fac

  10  continue
         write(*,*) 'Dame n: '
         read (*,*) n
         if (n.gt.0) then
            write(*,*) ' El factorial de', n, ' es', fac(n)
            goto 10
         endif
c     End of loop

      stop
      end
(Tip: Se tiene que usar un ciclo para implementar la función ya que Fortran 77 no soporta llamadas recursivas.)

Ejercicio B
Escribir una subrutina cuad que tome tres números reales a,b,c como entrada y encuentre las raíces de la ecuación ax**2 + bx + c = 0. Si las raíces son complejas, se deberá mostrar un mensaje de error como el siguiente:
      write(*,*) 'Advertencia: Raices Complejas.'
También se deberá escribir un programa main que pruebe la subrutina.


 [10. Arreglos ]  [Tutorial de Fortran]  [12. Entrada/salida Básica ]