Introdução à Programação na linguagem F Jaime Ramos, Amílcar Sernadas e Paulo Mateus DMIST, Setembro de 2005 Capítulo 3 Unidades de programa: módulos Objectivos Unidades de programa: módulos. Funções como argumento de funções. Unidades de programa A definição de funções e subrotinas em F pode ser feita dentro do corpo principal do programa, como foi ilustrado atrás, ou fora deste, numa unidade à parte chamada módulo. Um módulo é uma unidade de programa que contém, entre outras, definições de funções e subrotinas, e que não pode ser executada directamente, podendo apenas ser utilizada por outros programas. A vantagem de um módulo é que pode ser utilizado por diferentes programas sem necessidade de repetir o código. Considerem-se as funções sobre matrizes definidas no capítulo anterior. Podemos definir um módulo que disponibilize estas funções e que poderá ser utilizado por um ou mais programas que necessitem de algumas dessas funções e subrotinas sem ser necessário repetir o respectivo código. Apresenta-se em seguida um módulo que disponibiliza algumas funções sobre matrizes. module mmatrices public :: somaEl, prodEscalar, somaM, prodM, pesquisa private :: prodLC contains function somaEl(m) result(x) real, dimension(:,:), intent(in) :: m real :: x integer :: i, j x=0 do i=1,size(m,1) do j=1,size(m,2) x=x+m(i,j) end do end do end function somaEl function pesquisa(x,m) result(b) real, dimension(:,:), intent(in) :: m real, intent(in) :: x logical :: b integer :: i, j b=.false. do i=1,size(m,1) do j=1,size(m,2) if (x==m(i,j)) then b=.true. exit end if end do if (b) then exit end if end do end function pesquisa subroutine prodEscalar(m,x) 2 real, dimension(:,:), intent(inout) :: m real, intent (in) :: x integer :: i,j do i=1,size(m,1) do j=1,size(m,2) m(i,j)=m(i,j)*x end do end do end subroutine prodEscalar function somaM(m1,m2) result(a) real, dimension(:,:), intent(in) :: m1,m2 real, dimension(size(m1,1),size(m1,2)) :: a integer :: i,j if ((size(m1,1)==size(m2,1)).and.(size(m1,2)==size(m2,2))) then do i=1,size(m1,1) do j=1,size(m1,2) a(i,j)=m1(i,j)+m2(i,j) end do end do else print *,"Erro! As matrizes não são da mesma forma" end if end function somaM function prodM(m1,m2) result(a) real, dimension(:,:), intent(in) :: m1,m2 real, dimension(size(m1,1),size(m2,2)) :: a integer :: i,j if (size(m1,2)==size(m2,1)) then do i=1,size(m1,1) do j=1,size(m2,2) a(i,j)=prodLC(m1(i,1:size(m1,2)),m2(1:size(m2,1),j)) end do end do else print *,"Erro! As matrizes nao sao compativeis" end if end function prodM function prodLC(l,c) result(x) real, dimension(:), intent(in) :: l,c real :: x integer :: k x=0 do k=1,size(l) x=x+l(k)*c(k) end do end function prodLC end module mmatrices O modulo anterior é constituido por duas partes: a primeira parte, entre o início do módulo e a instrução contains; a segunda parte, desde a instrução contains até ao fim do módulo. Na primeira parte declaram-se eventuais módulos que este módulo possa utilizar, funções e subrotinas que o módulo disponibiliza (através da instrução public), funções e subrotinas auxiliares (através da instrução private), declarações de variáveis, entre outras de que veremos exemplos adiante. No caso 3 do exemplo, o módulo apenas disponibiliza as funções e subroutines somaEl, prodEscalar, somaM, prodM e pesquisa, já descritas anteriormente, através da declaração public :: somaEl, prodEscalar, somaM, prodM, pesquisa e define ainda uma função auxiliar, prodLC, que embora seja implementada no módulo não pode ser utilizada fora deste pois foi declarada como privada, através da declaração private :: prodLC Na segunda parte, definem-se as funções e subrotinas do módulo, quer as que são disponibilizadas pelo módulo quer as auxiliares. As funções auxiliares apenas podem ser referênciadas dentro do módulo, por outras funções, não sendo conhecidas fora deste. No caso do módulo anterior, a função prodLC apenas pode ser utilizada dentro do módulo (como de facto é, pela função prodM), não podendo ser utilizada por nenhum programa que utilize este módulo. Um programa pode utilizar um módulo através da instrução use. O programa seguinte utiliza o módulo mmatrices para multiplicar duas matrizes. program testeProd use mmatrices real, dimension(2,2) :: m1,m2,m3 m1(1,1:2)=(/ 1.0,2.5 /) m1(2,1:2)=(/ 4.5,1.1 /) m2(1,1:2)=(/ 1.3,2.3 /) m2(2,1:2)=(/ 5.5,0.5 /) m3=prodM(m1,m2) print *,m3(1,1:2) print *,m3(2,1:2) end program testeProd Ao utilizar o módulo mmatrices, o programa anterior tem à disposição todas as funções disponibilizadas (públicas) por este módulo. A compilação do programa anterior é, no entanto, um pouco mais complexa. Em primeiro lugar, há que compilar o módulo. Como o resultado não é um ficheiro executável, há que indicar isso aquando da compilação, através da opção –c. A instrução anterior gera um ficheiro mmatrices.o. Cada módulo só precisa de ser compilado quando é criado e de cada vez que é alterado (mas não quando é utilizado). Uma vez compilado o módulo, podemos compilar o programa. O processo de compilação é semelhante ao visto anteriormente, sendo apenas necessário acrescentar o nome do(s) módulo(s) que o programa utiliza. A estrutura genérica de um módulo é a seguinte: module nome [use módulos] public :: nomes private :: nomes outras declarações contains definição de funções e subrotinas end module nome Funções como argumento de procedimentos 4 Tal como foi visto anteriormente, é muitas vezes necessário definir funções ou subrotinas cujos parâmetros são eles próprios funções. Pretende-se definir uma função integrate que recebe como argumentos uma função real de variável real f e dois números reais a e b e um número inteiro n e calcula o integral de f no intervalo [a,b] (dividido em n subintervalos), tal como foi descrito anteriormente. A declaração de um parâmetro de tipo função é feita através da declaração da interface da função, onde se indicam quais os tipos dos parâmetros e qual o tipo do resultado. No caso presente, pretende-se que o parâmetro f seja uma função com um parâmetro real e devolvendo um número real, o que é obtido através da declaração seguinte: interface function f(x) result(y) real, intent(in) :: x real :: y end function f end interface Uma solução possível para a função integrate, disponibilizada pelo módulo mnumeric.f95, é a seguinte: function integrate(f,a,b,n) result(y) interface function f(x) result(y) real, intent(in) :: x real :: y end function f end interface real, intent(in) :: a,b integer, intent(in) :: n real :: y real:: d,x,s integer :: i d=(b-a)/n s=0 x=a do i=1,n s=s+f(x) x=x+d end do y=d*s end function integrate O funcionamento desta função já foi descrito anteriormente. A principal diferença reside na necessidade de declaração dos parâmetros e das variáveis auxiliares e, em particular, na declaração do parâmetro f. Repare-se que, uma vez declarado, este parâmetro pode ser usado como uma função real de variável real, como acontece na expressão s=s+f(x). O programa seguinte utiliza esta função para calcular o integral da função quad (definida pela expressão analítica -x2-x+2), disponibilizada no módulo mquad. program testintegrate use mnumeric use mquad real :: a,b integer :: n read *,a read *,b 5 read *,n print *,"O integral da funcao -x^2-x+2" print *,"no intervalo [",a,",",b,"]" print *,"e: ",integrate(quad,a,b,n) end program testintegrate O programa solicita ao utilizador os extremos do intervalo bem como o número de subdivisões pretendido e calcula o respectivo integral, de acordo com o método apresentado. Seguem-se alguns exemplos de execução: -2.0 2.0 100 O integral da funcao -x^2-x+2 no intervalo [ -2.0000000 , e: 2.7456055 -3.0 3.0 10000 O integral da funcao -x^2-x+2 no intervalo [ -3.0000000 , e: -5.9991608 2.0000000 ] 3.0000000 ] Note-se que a função que vai ser passada como argumento (neste caso a função quad) tem que estar definida num módulo diferente do da função que a vai receber como parâmetro (neste caso, a função integrate). Com efeito, a função quad está definida no módulo mquad e a função integrate está definida no módulo mnumeric. 6