Sei sulla pagina 1di 33

Elementos da Linguagem Fortran 90 Notas de Introduo Programao, 2002-03 Fernando M.S.

Silva Fernandes Departamento de Qumica e Bioqumica, FCUL

Estas notas sistematizam a parte das aulas tericas de Introduo Programao (ano lectivo de 2002/03) dedicada linguagem Fortran e tm uma estreita ligao com os trabalhos realizados durante as aulas prticas. Assim, pressupoem que os alunos tenham assistido s aulas tericas e realizado os trabalhos prticos. Sublinha-se, no entanto, que o texto presente no deve constituir uma alternativa consulta da bibliografia indicada. De facto, ele apenas menciona, e tenta esclarecer, alguns dos tpicos mais elementares do Fortran 90. Por outro lado, os alunos devero acompanhar a leitura destas notas com as listagens dos programas que escreveram e executaram nas aulas prticas bem como de outras notas j entretanto editadas e distribudas. 1. Introduo A memria central de um computador constituda por um conjunto de clulas fsicas, cada uma com um endereo de modo a que o sistema possa aceder a cada uma delas e processar a informao nelas contida. Essa informao constituda por nmeros, caracteres ou instrues. Qualquer destes tipos de informao, assim como os endereos das clulas, so codificados em binrio, isto , em termos de 0s e 1s (bits), mais propriamente em termos da representao fsica dos 0s e 1s (recorde-se a analogia com uma lmpada acesa ou apagada). Cada clula pode conter um nmero mximo de bits, designado pelo comprimento da clula. No incio da era dos computadores (nas dcadas de 40 e 50 do sculo XX) os programadores eram forados a codificar toda a informao em binrio, e a representla fsicamente atravs da aco de ligar ou desligar os interruptores correspondentes a cada bit de informao. Tratava-se de um trabalho rduo e, obviamente, sujeito a frequentes erros. Em 1954, John Backus, da empresa IBM, iniciou o projecto de desenvolvimento de uma linguagem de alto nvel (high-level language) designada por FORTRAN (de FORmula TRANslation) cujo objectivo era libertar os programadores da codificao directa em binrio. Assim, um programa em Fortran no mais do que um texto (programa fonte) escrito numa linguagem com regras de sintaxe semelhantes s da nossa linguagem natural e apropriadas para exprimir, de um modo simples, as expresses matemticas. O programa fonte seguidamente submetido a um tratamento especial, e praticamente automtico, designado por compilao, que faz a traduo da linguagem Fortran para cdigo binrio (programa executvel) o qual , ento, submetido mquina para processamento. A compilao realizada por um programa especial (compilador) residente no sistema o qual foi antecipadamente escrito por algum, especialista na matria. Actualmente, os programadores apenas tm de preocupar-se em escrever o programa fonte em Fortran (para isso tero de aprender a sintaxe e a semntica da linguagem), saber invocar o respectivo compilador e submeter o programa executvel ao processamento. O problema fundamental em programao pode ser esquematizado do seguinte modo:

Dados

funo computvel (programa)

Resultados

Isto , temos um conjunto de dados a partir dos quais, atravs de uma funo computvel (programa), queremos produzir um conjunto de resultados. A funo computvel, ou programa, tem de obedecer s regras impostas pela sintaxe e semntica da linguagem. Em primeiro lugar, teremos de descrever o tipo de dados que vo ser armazenados nas clulas da memria central. Isso feito num conjunto de instrues, escritas no incio do programa, designadas por declaraes. Depois, teremos de descrever as aces necessrias para armazenar os dados, operar sobre eles, armazenar os resultados dessas operaes e visualiz-los (escrita no monitor, no disco, em papel ou transferi-los para um programa de grficos). Isso feito num conjunto de instrues, escritas aps as declaraes, e que se designam por comandos. Assim, podemos definir um programa como um conjunto de declaraes seguidas de um conjunto de comandos. Tanto as declaraes dos dados como os comandos so obrigatoriamente limitadas por declaraes que indicam explcitamente o incio e o fim do programa. Assim, a estrutura geral de um programa principal em Fortran tem obrigatoriamente a seguinte ordem: program nome_do_programa Declaraes dos dados Comandos end program nome_do_programa Todas as instrues em Fortran so obrigatoriamente escritas em ingls (program, end program, etc.) em letra maiscula, minscula ou mista. O nome_do_programa um identificador escolhido pelo programador de acordo com as regras que veremos adiante. H dois tipos de identificadores: os nomes escolhidos pelo programador e as palavras-reservadas. Os primeiros designam, por exemplo, os nomes dos programas, das variveis e das constantes, no sendo obrigatrio que tenham significado em ingls. As palavras-reservadas, pelo contrrio, designam instrues (declaraes e comandos) sendo obrigatoriamente escritas em ingls. Assim, program ou end program so palavras-reservadas, ao passo que se entendermos dar ao nome_do_programa a designao chaparro (perfeitamente aceitvel em Fortran) trata-se de um nome escolhido pelo programador com significado em portugus, mas no em ingls. No que se segue destacaremos as palavras-reservadas em negrito. 2. Nomes escolhidos pelo programador Comeam obrigatoriamente por uma letra do alfabeto ingls (A-Z ou a-z, inclundo o K, W e Y) seguidos, se assim se quiser, por um conjunto de letras, dgitos (0-9) ou underscores at ao mximo de 31 caracteres. Espaos em branco no so admitidos. Tal como em ingls, no permitida acentuao nos nomes. Assim, por exemplo, so perfeitamente admissveis os seguintes nomes: ANA_MARIA, Ana_Maria, soma, SOMA12, Somatorio Os seguintes, contudo, no so vlidos: Somatrio, PENSO, soma*3, Ana Maria

Um bom princpio em programao escolher nomes com significado nas aplicaes concretas. 3. Tipos de dados O Fortran tem uma considervel versatilidade no que se refere ao tipo de dados que pode manipular. Um tipo de dados define um conjunto de valores com determinadas caractersticas. Para comear, destacaremos quatro tipos fundamentais ou padro: inteiro, real, caracter e lgico. Mais tarde, referiremos outros tipos mais elaborados nomeadamente: array (vector ou quadro), complexo, derivado e apontador (pointer). 3.1 Tipo inteiro Conjunto de nmeros inteiros positivos e negativos. A escrita dos inteiros obedece a regras. Assim, so vlidas as seguintes constantes inteiras: - 435 ; + 256 ou 256 ; 10000 mas invlidas: 10,000 ou 10.000 ou 10 000 Na escrita de inteiros no so admitidos os separadores frequentemente utilizados na escrita normal. 3.2 Tipo real Conjunto de nmeros reais positivos e negativos. A escrita pode ser efectuada em notao decimal ou cientfica sujeita, tambm, a regras. Assim, em notao decimal, so vlidas as seguintes constantes: +2.56 ou 2.56 ; -3.4567 mas invlidas: +2,56 ou 2,56 ; -3,4567 A notao decimal no admite a vrgula como usual em Portugal. No que se refere notao cientfica, uma vez que o Fortran no admite sobre ou subndices, o smbolo: 1.34 10-5 no vlido. Em vez dele, escreve-se: 1.34E-5 ou 1.34e-5, onde o E, ou e, representa a base 10. Cuidado, este E nada tem a ver com a base dos logaritmos neperianos!

3.3 Tipo caracter Sequncia de smbolos (pertencentes ao conjunto de caracteres do Fortran; ver bibliogafia indicada) escritos entre aspas ou apstrofes. Exemplos deste tipo so as constantes: Ana Maria ; media*/ ; 2567 ou 2567 Nota importante: 2567 no designa um nmero, mas apenas uma sequncia de caracteres. Repare-se que o espao em branco em Ana Maria tambm um caracter. 3.4 Tipo lgico Conjunto dos dois valores lgicos verdadeiro e falso (true e false) escritos entre pontos: .TRUE. ; .FALSE. ou .true. ; .false. ou qualquer combinao de maisculas e minsculas. 4. Declarao de variveis e sua inicializao. Comandos de atribuio e escrita. Os diferentes tipos de dados so armazenados nas clulas da memria central as quais, a nvel do Fortran, se identificam por nomes escolhidos pelo programador, de acordo com as regras que vimos anteriormente. Esses nomes caracterizam o que se designa, de um modo geral, por variveis uma vez que os valores que representam podem ser alterados. Assim, cada varivel ser de um tipo inteiro, real, caracter ou lgico, de acordo com o tipo de dados que vai conter. obrigatrio, ento, que declaremos os tipos de variveis que vamos utilizar num programa. De facto, o processamento dos respectivos valores depende do seu tipo. Suponhamos que num dado programa (a que queremos dar o nome: primeiro) temos as variveis i, rota (tipo inteiro) ; xm, yy (tipo real) ; nome, morada, cidade (tipo caracter) e bom, mau (tipo lgico). As suas declaraes sero feitas do seguinte modo, seguindo e estrutura geral que vimos anteriormente: program primeiro ................................... integer :: i, rota real :: xm, yy character (len=20) :: nome, morada logical :: bom, mau ..................................... ..................................... end program primeiro Na declarao das variveis tipo caracter (len=20) indica explicitamente o nmero mximo de caracteres (comprimento, length) admitidos na sequncia. Uma vez declaradas, as variveis tm de ser inicializadas, pois de contrrio permanecero indefinidas. De facto, nunca processar variveis sem as inicializar previamente. Existem vrios processos para inicializar variveis. Por enquanto indicaremos o processo de atribuio. A atribuio (assignement) um comando, isto , uma ordem para a realizao de uma operao (colocar um determinado valor numa

clula da memria), que em Fortran se designa pelo smbolo = o qual, como veremos em breve, no deve ser interpretado como uma igualdade lgica. Podemos, ento, inicializar as variveis anteriores por atribuio. Por exemplo: i=3 rota = 11 xm = 21.45 yy = 1.25e-15 nome = Ana Maria morada = Rua Cidade Nova, 25 bom = .false. mau = .true. xm = xm +1 Os dados introduzidos nas diferentes variveis esto perfeitamente de acordo com os tipos declarados anteriormente. Repare-se, no entanto, no ltimo comando: xm = xm + 1 A varivel xm tinha sido inicializada com o valor 21.45 no 3 comando. Experimentemos interpretar o ltimo comando como xm igual a xm mais 1. obviamente um disparate! Assim, o comando de atribuio = no pode ser interpretado como uma igualdade lgica. Trata-se, sim, de uma ordem para a realizao da seguinte operao: ver o valor que se encontra em xm, adicionar-lhe 1 e colocar o resultado (22.45) em xm. O seu valor inicial (21.45) substituido por 22.45. Eis o princpio do gravador magntico! Trata-se, no fundo, de uma reatribuio. E mostra que, de facto, xm uma varivel pois o seu valor foi alterado. Uma vez inicializadas as variveis, deveremos assegurar-nos que elas contm, efectivamente, os valores desejados. Para isso, teremos de imprimir os respectivos valores em qualquer meio disponvel para o efeito. Isso feito atravs de comandos de escrita: write (*,*) Valor de i =, i, Valor de rota =, rota write (*,*) Valor de xm =, xm, Valor de yy =, y write (*,*) O nome :, nome, A morada : , morada write (*,*) Afinal bom parece , bom, E mau parece, mau Por cada comando write impressa uma nova linha. Assim, os valores das variveis i e rota so impressos numa linha, os das variveis xm e yy na linha seguinte e assim sucessivamente. Repare-se que antes dos valores armazenados em cada varivel, mandamos imprimir constantes do tipo caracter (as sequncias entre aspas) que tm por fim anteceder (como mensagens escritas) os valores das variveis que se seguem. Se omitirmos essas constantes, ento apenas sero escritos os valores armazenados nas variveis sem qualquer indicao especfica. As constantes do tipo caracter foram livremente escolhidas por ns e, como tal, poderiam ter sido outras quaisquer. Os * no comando tm o seguinte significado: o primeiro diz respeito ao meio de escrita e o segundo ao formato pretendido para os valores escritos, isto , nmero de casas decimais, espaos, etc. Se o primeiro * for mantido no comando, isso significa que os valores sero escritos no meio por defeito (o monitor). Se desejarmos outro meio de escrita (o disco rgido, por

exemplo) teremos de substituir esse * por um nmero inteiro (3, por exemplo) que designa o canal de comunicao com o disco. Nesse caso, previamente, teremos de indicar a respectiva associao com o ficheiro onde os valores vo ser escritos, atravs da instruo: open ( unit = 3, file = nome_do_ficheiro, status = new ) A instruo open pode conter mais especificaes (ver bibliografia indicada) que sero exploras durante as aulas prticas. Ento, o comando de escrita ser: write (3, *) lista_de_output Veja-se o programa sobre a lei do arrefecimento de Newton que realizmos nas aulas prticas. Se o segundo * for mantido isso significa que os valores sero escritos no respectivo meio em formato livre, o qual depende do compilador utilizado. Por exemplo, os nmeros reais em notao decimal sero escritos, em geral, com cerca de 7 algarismos significativos justificados direita. Se desejarmos um formato determinado (um nmero de casas decimais fixo, por exemplo) teremos de substitur o segundo * por especificaes adequadas que veremos adiante. A sintaxe geral do comando write , ento, write(numero_da_unidade , especificaes_de_formato) lista_de_output No caso de se utilizar o meio de escrita por defeito (o monitor) existe um caso particular do comando write: print *, lista_de_output onde o *, corresponde ao segundo * do comando write, e lista_de_output a lista de objectos (variveis, constantes ou expresses) que se pretendam escrever. Assim, para escrever no monitor, em formato livre, podemos utilizar: print *, Valor de i =, i, Valor de rota =, rota Estamos em condies de terminar o primeiro programa primeiro em Fortran: Program primeiro ! Este programa declara variaveis, inicializa-as e escreve os seus conteudos Implicit none ! Obriga a declarar explicitamente as variaveis ! Declaremos as variaveis Integer :: i, yota Real :: xm, yy, z Character (len=20) :: nome, morada ! (len=20) indica o numero maximo de caracteres Logical :: bom, mau ! Inicializemos as variaveis i=3 rota = 11 xm = 21.45

yy = 1.25e-15 nome = Ana Maria morada = Rua Cidade Nova, 25 bom = .false. mau = .true. xm = xm +1 ! Escrever os valores das variaveis antecididos por mensagens write(*,*) Os valores das variaveis inicializadas por atribuicao sao: write (*,*) Valor de i =, i, Valor de rota =, rota write (*,*) Valor de xm =, xm, Valor de yy =, y write (*,*) O nome :, nome, A morada : , morada write (*,*) Afinal bom parece , bom, E mau parece, mau End program primeiro Aps o ncio do programa temos a declarao implicit none, a qual obriga o programador a declarar o tipo de todas as variveis. Perante essa declarao, se o programador se esquecer de declarar algumas variveis utilizadas, o compilador recusar-se- a produzir o programa executvel, emitindo uma mensagem onde indica quais as variveis no declaradas explcitamente.Trata-se de uma instruo para evitar o seguinte. Nas primeiras verses da linguagem Fortran a declarao explcita de variveis no era obrigatria, sendo feita implcitamente de acordo com a 1 letra do nome da varivel. Uma vez que muitas bibliotecas de programas em Fortran, de grande importncia cientfica e no s, ainda se encontram escritas nas verses iniciais da linguagem, entenderam as sucessivas Comisses de Normalizao das recentes verses do Fortran manter a compatibilidade com as mais antigas. Sucede que a no declarao explcita das variveis pode conduzir a erros difceis de detectar com a consequente perda de tempo e de produtividade. Durante as aulas prticas veremos algumas razes para que se deva impor a declarao explcita de todas as variveis. Finalmente, inclumos no programa alguns comentrios aps !. Tratam-se de anotaes importantes para quem leia os programas. Podem ser includas em quaisquer pontos do texto do programa desde que antecedidas por !. O compilador no as considera, mas os humanos so prdigos em esquecerem-se do que fizeram ontem! Assim, os comentrios so justamente e somente para eles! 5. Declarar dados como constantes Vimos que uma vez declarada uma varivel, ela pode ser inicializada por atribuio e que mais tarde o seu valor pode ser alterado por reatribuio (recorde-se o que se passou com a varivel xm do programa anterior). Suponhamos que num programa temos a necessidade de incluir constantes universais como, por exemplo, o nmero de Avogadro e o para calcular o nmero de moles (a partir de um dado numero de molculas) e a area de um crculo de raio R. Podemos declarar as seguintes variveis: real :: Numero_de_Avogadro, Pi real :: Numero_de_moleculas, Numero_de_moles, Raio_do_circulo, Area_do_circulo e atribuir-lhes os respectivos valores por atribuio: Numero_de_Avogadro = 6.022e23

Pi = 3.1415695 Numero_de_moleculas = 12.6e23 Raio_do_circulo = 5.75 Podemos, agora, utilizar esses valores para calcular o nmero de moles e a rea do crculo: Numero_de_moles = Numero_de_moleculas / Numero_de_Avogadro Area_do_circulo = Pi * Raio_do_circulo ** 2 At aqui, tudo bem. Mas suponhamos que, inadvertidamente, antes da expresso para o clculo da rea inclumos o comando: Pi = 0.0 O comando perfeitamente aceitvel, o compilador no acusar erros, mas executada uma reatribuio varivel Pi. Como resultado obteremos uma rea nula para o crculo! De modo a precaver situaes destas, o Fortran permite que se declarem nomes como constantes (named-constants). Assim, as declaraes do nmero de Avogadro e de devero ser: real, parameter :: Numero_de_Avogadro = 6.022e23, Pi = 3.1415695 Deste modo, uma eventual reatribuio, como a que vimos anteriormente para Pi, ser impedida pelo compilador pela emisso de uma mensagem de erro e a no produo do programa executvel. O problema dos clculos que exemplificmos pode ser traduzido no seguinte programa: program Avogadro_Area implicit none real, parameter :: Numero_de_Avogadro = 6.022e23, Pi = 3.1415695 real :: Numero_de_moleculas, Numero_de_moles, Raio_do_circulo, Area_do_circulo Numero_de_moleculas = 12.6e23 Raio_do_circulo = 5.75 Numero_de_moles = Numero_de_moleculas / Numero_de_Avogadro Area_do_circulo = Pi * Raio_do_circulo ** 2 write(*,*) Eis o numero_de_moles = , Numero_de_moles write(*,*) Eis a area do circulo = , Area_do_circulo end program Avogadro_Area Com este programa temos assegurado que qualquer alterao inadvertida do nmero de Avogadro, ou de , ser imediatamente detectada e rejeitada pelo compilador. 6. Comando de Leitura O programa anterior tem um inconveniente. Sempre que queiramos calcular novos nmeros de moles e reas de crculos, teremos de editar o texto do programa, atribuir s variveis Numero_de_moleculas e Raio_do_circulo novos valores e recompilar o program. De facto, os comandos de atribuio so operaes definidas internamente. Ser muito importante, ter a possibilidade de introduzir os valores das variveis atravs

de um meio exterior evitando, assim, a necessidade de alterar o texto do programa de cada vez que tenhamos novos valores. Isso possvel, atravs do comando read: read (*,*) Numero_de_moleculas read (*,*) Raio_do_circulo Os * tm o mesmo significado que no comando write. Somente que, no caso da leitura, o meio por defeito para introduzir os dados o teclado. Do modo anlogo escrita, se quisermos ler de um ficheiro em disco, teremos de fazer previamente a associao entre o nmero da respectiva unidade e o nome do ficheiro em disco, atravs da instruo open. A sintaxe geral do comando read : read (numero_da_unidade, especificaes_de_formato) lista_de_variveis Quando se utiliza o teclado, existe um caso particular do comando read: read *, lista_de_variveis O * poder ser substitudo por especificaes de formato se a leitura for formatada. Substituindo os comandos de atribuio para as variveis Numero_de_moleculas e Raio_do_circulo, no programa anterior, por comandos read ficaremos com um programa geral que no necessrio alterar como no caso de inicilizarmos as referidas variveis por atribuio. 7. Expresses aritmticas Consideremos os seguintes comandos dos exemplos anteriores: rota = 11 xm = xm +1 Numero_de_moles = Numero_de_moleculas / Numero_de_Avogadro Area_do_circulo = Pi * Raio_do_circulo ** 2 Tratam-se de comandos de atribuio em que os membros direitos no so mais do que expresses aritmticas (inteiras ou reais), isto , um conjunto de constantes e/ou variveis associadas por operadores aritmticos. Em Fortran, existem os seguintes operadores aritmticos, que se listam pela ordem hierrquica de execuo: exponenciao: ** multiplicao e diviso: * e / soma e subtrao: + e Na expresso da rea do crculo, a exponenciao realizada em primeiro lugar e s depois a multiplicao. Suponhamos que queramos a multiplicao realizada em primeiro lugar. Ento, tal como em Aritmtica, escreveramos:

Area_do_circulo = (Pi * Raio_do_circulo) ** 2 Isto , uma expresso entre parntesis curvos ultrapassa a hierarquia natural dos operadores. E podemos utilizar mais nveis de parntesis sempre que necessrio. Mas s parntesis curvos. No so admitidos parntesis rectos ou chavetas. No caso presente, a ltima expresso correcta sob o ponto de vista da sintaxe, mas no da semntica (significado intrnseco). De facto, com a ltima expresso no obteramos a rea verdadeira do crculo. Sempre que escrevamos expresses deveremos assegurar-nos que a semntica correcta. Sempre que necessrio utilizar diferentes nveis de parntesis curvos de modo a ultrapassar a hierarquia natural dos operadores aritmticos. Existem outros tipos de expresses, alm das aritmticas, como por exemplo, expresses do tipo caracter e expresses lgicas ou booleanas. Os resultados dessas expresses so, respectivamente, constantes tipo caracter ou do tipo lgico que vimos em pargrafos anteriores. A seu tempo, referiremos essas expresses. A sintaxe geral do comando de atribuio , ento: Nome_de_varivel = expresso 8. Arrays. Estrutura iterativa com ndice. As variveis que vimos at agora foram identificadas por um nome e podiam armazenar um nico valor. Como tal, designam-se frequentemente por variveis escalares. Suponhamos as expresses bem conhecidas para o somatrio e multiplicatrio:

n
i =1

x i = x1 + x 2 + ... + x n

y j = y1.y 2 ...y n
j=1

As variveis xi e yj designam-se por variveis indexadas, isto , so designadas pelo mesmo nome e distinguem-se umas das outras atavs do valor de um ndice. O Fortran permite declarar este tipo de variveis, designado por array (vector ou quadro) as quais podem conter os tipos padro de dados que vimos inicialmente. Tratase de um tipo estruturado de dados apropriado para manipular vectores e matrizes de diferentes dimenses. Por exemplo, a seguinte declarao:
real, dimension (5) :: x

reserva 10 clulas na memria central cujos nomes so: x(1), x(2), x(3), x(4), x(5) Diz-se, ento, que se definiu um array real (ou um vector) de dimenso 1 com extenso 5. Essas clulas podem agora ser inicializadas por atribuio ou leitura, por exemplo: x(1) = 4.5 ou read(*,*) x(1)
10

x(2) = 3.2 x(3) = 2.0 x(4) = 6.1 x(5) =1.2

ou ou ou ou

read(*,*) x(2) read(*,*) x(3) read(*,*) x(4) read(*,*) x(5)

Tambm se pode inicializar o array anterior com a seguinte atribuio global: x = ( /4.5, 3.2, 2.0, 6.1, 1.2/ ) O membro direito da atribuio designa-se por constante tipo vector (array constant). claro que se tivermos um vector com uma extenso muito grande os processos de inicializao anteriores tornam-se impraticveis. Necessitamos um comando estruturado que tire partido do ndice da varivel e que no nos obrigue a mencionar explcitamente cada elemento do vector. Uma estrutura que permita escrever uma nico comando e a repeti-lo (a iter-lo) tantas vezes quantas as necessrias. Tal estrutura o ciclo iterativo com ndice: do i = 1, 5 read (*,*) x(i) end do A iterao controlada pelo ndice inteiro i, o qual varia o seu valor de 1 at 5. Podemos, agora escrever dois programas bsicos para calcular somatrios e multiplicatrios. program somatorio implicit none integer, parameter :: n = 5 real, dimension (n) :: x integer :: i real :: soma soma = 0.0 ciclo_soma : do i = 1, n read (*,*) x(i) soma = soma + x(i) end do ciclo_soma write (*,*) soma end program somatorio program multiplicatorio implicit none integer, parameter :: n=5 real, dimension (n) :: y integer :: j real :: mult mult = 1.0 ciclo_mult : do j =1, n read (*,*) y(j) mult = mult * y(i) end do ciclo_mult write (*,*) mult end program multiplicatorio

Declarmos n como uma constante inteira, a qual define a extenso dos vectores. Alm disso, o Fortran permite-nos escolher nomes para os ciclos (de acordo com as regras j apresentadas). Reparar que o nome do ciclo no seu incio obrigatoriamente seguido de :. A nomeao dos ciclos, embora opcional, muito importante, pois torna os programas mais legveis e evita erros, principalmente quando os programas so de grande extenso e tm vrios ciclos. A extenso dos vectores foi definida atravs de uma constante declarada antecipadamente. claro que por esse processo, se quisermos vectores de outra extenso teremos que editar o programa, alterar a constante n e recompilar. Tal no prtico e retira a generalidade aos programas. primeira vista, poderiamos pensar em fazer:

11

read (*,*) n real, dimension (n) :: x Porm, isso no permitido em Fortran, onde todas as declaraes tm obrigatoriamente que anteceder os comandos. Ora read... um comando e real, dimension... uma declarao, estando desse modo a ordem incorrecta. A razo da proibio a seguinte. Como sabemos existem dois compassos temporais no processamento de um programa: a compilao e a execuo. As declaraes so instrues para o compilador e sero processadas no tempo de compilao. Pelo contrrio, os comandos s so processados no tempo de execuo. Cada macaco no seu galho! Para evitar esta situao o Fortran permite a declarao dos chamados arrays alocveis (allocatable arrays). Eles permitem a leitura da extenso dos arrays e a alocao da respectiva memria no tempo de execuo: real, dimension ( : ), allocatable :: x read (*,*) n ! um comando allocate ( x(n) ) ! outro comando ! uma declarao

So permitidos arrays at 7 dimenses (com 7 ndices diferentes). Por exemplo, se quisermos tratar matrizes poderemos definir arrays a duas dimenses: real, dimension ( :, :), allocatable :: z ! declarao para um array z a duas dimenses read (*,*) m, n ! ler as extenses de cada dimenso: numero de linhas e colunas allocate ( z (m,n) ) ! memoria alocada no tempo de execuo. O elemento generico da ! matriz: a(i, j) com i = 1, 2, ...., m e j = 1, 2, ..., n Suponhamos que atribuimos a m o valor 3 e a n o valor 2. Definimos, assim, um array (matriz ou quadro) a duas dimenses, com 3x2 = 6 elementos (clulas). O nmero total de clulas designa-se por tamanho (size) do array. As extenses de cada dimenso so respectivamente 3 e 2. O nmero de dimenses de um array designa-se por classe ou grau (rank) do array. As clulas do array so reservadas em posies da memria central, designadas sucessivamente do seguinte modo : z(1,1), z(2,1), z(3,1), z(1,2), z(2,2) e z(3,2). Para inicializar as variveis da matriz (por leitura) pertencentes a cada linha i: ciclo_de_linha : do i = 1, m ciclo_de_coluna : do j = 1, n read (*,*) z (i, j) end do ciclo_de_coluna end do ciclo_de_linha O cdigo anterior exemplifica os chamados ciclos aninhados (nested loops) nos quais o ciclo interno anda mais rapidamente do que o externo. Quer dizer, quando o ndice i adquire um valor, o ndice j percorre todos os valores possveis de 1 a n e, assim, sucessivamente. O cdigo para escrever cada elemento da matriz poder ser: ciclo_de_linha : do i = 1, m ciclo_de_coluna : do j = 1, n

12

write (*,*) z (i, j) end do ciclo_de_coluna end do ciclo_de_linha Sabemos, no entanto, que por cada write temos uma nova linha impressa. Ento, o cdigo anterior escrever cada elemento da matriz numa linha diferente do monitor. O que, certamente, pretendemos que os elementos da mesma linha da matriz sejam escritos na mesma linha do monitor. Para isso, pode utilizar-se o ciclo iterativo implcito: ciclo_de_linha : do i = 1, m write (*,*) z (i, j) (j = 1, n) ! ciclo j implicito end do ciclo_de_linha Isto , para cada i teremos apenas um nico write, pois o ciclo implcito equivalente a: write (*,*) z(i, 1), z(i, 2), , z(i, n) portanto todos os elementos da mesma linha da matriz sero escritos na mesma linha do monitor. Em vez do ciclo implcito, pode utilizar-se mais sucintamente: write(*,*) z (i, :) z (i, : ) designa um subconjunto do array x. Neste caso, designa todos os elementos da linha i. Ver na bibliografia indicada o modo de definir e manipular os diferentes subconjuntos de arrays que se podem definir. Dois arrays com o mesmo nmero de dimenses (rank) e o mesmo nmero de elementos em cada dimenso dizem-se ter a mesma forma (shape). Finalmente, deve referir-se que a importncia dos arrays alocveis ultrapassa muito a utilizao que referimos anteriormente. De facto, representam um dos processos de definir memria dinmica no tempo de execuo. Na verdade, aps serem alocados podem ser posteriromente desalocados quando j no forem necessrios, atravs do comando deallocate (nome_do_array) permitindo, desse modo, libertar memria. Tal no possvel com os arrays definidos no tempo de compilao, os quais so estticos pois uma vez definidos, o respectivo espao de memria fica reservado at ao fim da execuo do programa. Os comandos allocate e deallocate admitem, tambm, especificaes que permitem controlar o sucesso ou insucesso da alocao e da desalocao da memria (ver bibliografia indicada). Neste contexto, o Fortran admite adicionalmente um modo ainda mais poderoso de alocar memria dinmica, atravs do tipo de dados apontador (pointer). 9. Ciclos iterativos sem ndice. Estruturas selectivas. Para utilizar um ciclo iterativo com ndice necessrio saber antecipadamente o nmero de iteraes. Contudo, em muitas situaes, esse nmero no conhecido. Recorde-se o mtodo das bisseces sucessivas, que utilizmos para encontar a raz de uma equao algbrica do 3 grau no problema da determinao do pH de uma mistura dum cido fraco com uma base forte. A subrotina (veremos o conceito de subrotina mais adiante) utilizada foi:

13

subroutine bissect (f, x1, x2, tol, xm, erro) .. ! abs ( argumentos ) e f ( argumentos ) so invocaes de funes que veremos adiante raiz : do xm=(x1+x2)/2.0 if ( abs (x1-x2) <= tol .or. f (xm) = = 0.0 ) exit reduz : if ( (f(x1)*f(xm) ) > 0.0) then x1 = xm else x2 = xm enf if reduz end do raiz ............................................................................................................. end subroutine bissect O ciclo deve terminar quando o comprimento do intervalo onde a raiz se encontra [x1, x2] for reduzido a um comprimento inferior tolerncia. O nmero de iteraes necesssrias desconhece-se inicialmente. Depende do comprimento do intervalo inicial e da tolerncia escolhida. Assim, no pode utilizar-se um ciclo iterativo com ndice. O Fortran admite ciclos iterativos controlados por uma expresso lgica, exemplificado no cdigo anterior, cuja sintaxe geral : nome_do_ciclo : do .................................... if (expresso_lgica) exit ...................................... end do nome_do_ciclo Uma expresso lgica pode tem como resultado um dos dois valores do tipo lgico: .true. ou .false. O comando selectivo ou condicional: if (expresso_lgica) exit o qual pode ser colocado em qualquer local (dependendo do problema concreto) dentro do ciclo, significa: se a expresso lgica for verdadeira terminar o ciclo e passar execuo dos comandos que se encontrem aps end do nome_do_ciclo. importante demonstrar que o ciclo iterativo com ndice um caso particular do ciclo iterativo sem ndice. A sintaxe geral para a iterao com ndice : nome_do_ciclo : do i = m, n, p ......................... end do nome_do_ciclo onde p o passo do ciclo. Se for omitido significa que o passo 1, isto : i = m, m+1, m+2, ..., n m<n ; i, m e n so variveis inteiras

14

Se for diferente de 1 e positivo: i = m, m+p, m+p+p, ...,n Se for negativo: i = m, m-p, m-p-p, ..., n m>n m<n

Vejamos que podemos escrever este ciclo na forma de um ciclo iterativo sem ndice. Suponhamos que p positivo: i=m nome_do_ciclo : do if ( i > n ) exit .. i=i+p end do nome_do_ciclo Conclui-se que o ciclo iterativo com ndice consta de uma inicializao do ndice, de um teste do seu valor e da sua incrementao. E , de facto, um caso particular do ciclo iterativo sem ndice como queriamos demonstrar. O comando if ( expresso_lgica ) exit um caso particular da estrutura selectiva geral: nome : if ( expresso_lgica) then ........................................ else ........................................ end if nome tambm utilizada no cdigo das bisseces sucessivas que vimos acima. Dentro de cada ramo then ou else so includos outros comandos. Se a expresso_lgica for verdadeira sero executados os comandos do ramo then, se for falsa sero executados os do ramo else. Trata-se, assim, de uma escolha alternativa. Pode suceder (como no problema da resoluo da equao do 2 grau) que dentro do ramo else exista outra estrutura condicional: if (expresso_lgica1) then ........................................ else if (expresso_lgica2) then ........................................... else .......................................... end if . end if

15

Neste caso, a escrita pode ser simplificada utilizando a estrutura else if (expresso_lgica) then e um end if , em vez de dois. if (expresso_lgica1) then ........................................ else if (expresso_lgica2) then ........................................... else ................................... end if A subrotina bissect refere abs(lista_de_argumentos) e f( lista_de_argumentos). Tratamse de invocaes de funes. As funes, assim como as subrotinas, so subprogramas, que juntamente com os mdulos, constituem unidades independentes do programa principal e so designadas de um modo geral por procedimentos. O conceito de procedimento, um assunto-chave em programao como veremos adiante. Antes, porm, analisemos as expresses lgicas que temos vindo a mencionar. 10. Expresses lgicas No pargrafo 7, vimos que uma expresso aritmtica um conjunto de variveis e constantes associadas por operadores aritmticos. O resultado de uma expresso aritmtica inteira ou real uma constante pertencente ao tipo inteiro ou real, conforme o tipo da expresso. De modo anlogo, uma expresso lgica uma conjunto de variveis e constantes associadas por operadores lgicos. O seu resultado uma constante lgica ( .true. ou .false. ) O Fortran admite duas variedades de operadores lgicos: os operadores relacionais e os operadores combinacionais. Os operadores relacionais so: Operadores lgicos relacionais Smbolo < > == <= >= /= Exemplos de expresses lgicas simples so: .true. ; x > 10.3 ; numero = = 500 Se x tiver o valor 15.6 a segunda expresso tem como resultado .true. ; se numero tiver o valor 300 a ltima expresso tem com resultado .false. A seguinte expresso lgica: B ** 2 >= 4.0 * A * C Significado menor do que maior do que igual a menor do que ou igual a maior do que ou igual a diferente de

16

contem simultaneamente operadores aritmticos e relacionais. Os operadores aritmticos so executados em primeiro lugar. Assim, a expresso equivalente seguinte: (B ** 2) >= (4.0 * A * C) Se A, B e C tiverem respectivamente os valores 2.0, 1.0 e 3.0, esta expresso lgica resultar em 1.0 >= 24.0 que tem com resultado .false. Quando se utilizam os operadores relacionais == ou /=, muito importante considerar que muitos valores reais no podem ser armazenados exactamente como veremos adiante. Isto implica que expresses formadas por comparao de quantidades reais com == so tm muitas vezes um resultado falso, embora essas quantidades sejam algebricamente iguais. Nesses casos devem utilizar-se os operadores > = ou < =. Relativamente aos dados tipo caracter, eles so codificados numericamente de forma a que se estabelea uma relao de ordem entre os diferentes caracteres (recorde-se o cdigo ASCII; ver bibliografia indicada). Assim: A < F ; 6 > 4 so expresses lgicas verdadeiras. Duas sequncias de caracteres so comparadas caracter a caracter usando esses cdigos numricos: cat < dog verdadeira, pois c menor do que d cat < cow verdadeira, pois a menor do que o June > July verdadeira pois n maior do que l Duas sequncias de caracteres de diferente comprimento so comparadas como se espaos em branco (codificados tambm numericamente) estivessem apensos sequncia mais curta. Assim: cat < cattle avaliada como cat < cattle a qual verdadeira pois o cdigo numrico do espao em branco precede os cdigos de todas as letras. Expresses lgicas compostas so formadas combinando expresses lgicas simples por operadores combinacionais:

17

Operador .not. .and. .or. .eqv. .neqv.

Operadores lgicos combinacionais Expresso lgica Definio .not. p True se p false ; false se p true p .and. q Conjuno: true se p e q so ambos true; false nos outros casos p .or. q Disjuno: true se p ou q ou ambos so true; false nos outros casos p .eqv. q Equivalncia: true se p e q so ambos true ou ambos false ; false nos outros casos p .neqv. q No equivalncia: true se um dos p ou q true e o outro false; false nos outros casos

Quando uma expresso lgica contem operadores aritmticos, realcionais e combinacionais as operaes so executadas na seguinte ordem: 1. Operaes aritmticas ( e funes) 2. Operaes relacionais 3. Operaes combinacionais com a ordem seguinte: .not., .and., .or., .eqv. (ou .neqv.) claro que esta ordem implcita pode ser alterada pelo uso de parntises curvos.

11. Fune e subrotinas. Mdulos 11.1. Funes Na subrotina do pargrafo 9, vimos invocaes das funes abs (lista_de_argumentos) e f (lista_de_argumentos). Funes so subprogramas ou seja unidades independentes do programa principal (o qual, como sabemos, escrito entre as declaraes program e end program), que executam tarefas especficas e que so invocados a partir do programa principal (ou de outros subprograms) tantas vezes quantas as necessrias. O Fortran dispe de dois tipos bsicos de funes : funes ntrinsecas e funes externas. As primeiras, como abs (argumentos), que retorna o valor absoluto de um nmero, fazem parte da prpria linguagem, isto da sua biblioteca intrinseca de subprogramas, e podem ser acedidas pela simples invocao dos seus nomes, desde o momento que saibamos a sintaxe correcta. Na bibliografia indicada, encontra-se uma listagem completa das funes intrnsecas do Fortran. Nas aulas prticas utilizmos vrias funes intrnsecas. As funes externas, pelo contrrio, no fazem parte da linguagem e tm de ser escritas pelos programadores. Suponhamos que temos um programa onde necessitamos calcular vrios factorais de nmeros inteiros. A funo factorial no intrinseca, logo teremos de a escrever: function factorial ( n ) implicit none integer :: factorial, i integer, intent (in) :: n

18

factorial = 1 do i = 1, n factorial = factorial * i end do end function factorial O cdigo de uma funo externa obrigatoriamente escrito entre as declaraes: function nome_da_funo (argumentos) e end function nome_da_funo e colocado, em geral, aps e end program nome_do_programa (do programa principal). Suponhamos, agora, o seguinte programa principal: program fact implicit none integer :: i, b, x, n, z, factorial read (*,*) x, n, z i = 40 * factorial (x) b = 3 * factorial (n) write (*,*) i, b, factorial (z) end program fact H vrios aspectos muito importantes que devemos realar. A varivel i da funo uma varivel local do subprograma funo e no acessvel ao programa principal. Assim, se eventualmente, o programa principal tiver uma varivel tambm chamada i, como o caso neste exemplo, so na realidade duas variveis distintas. O argumento da funo n, uma varivel de interface, ou seja, um dos meios possveis atravs do qual o progama principal e a funo comunicam entre si. Mas, repare-se, quando invocmos a funo por trs vezes, no programa principal, introduzimos trs nomes diferentes no argumento: x, n e z. Dois deles (x e z) so diferentes do nome que demos ao argumento da funo quando a definimos. O que na realidade se passa que enquanto a funo no for invocada o seu argumento, n, no mais do que um nome indefinido (dummy argument), mais propriamente um parmetro formal ou abstracto. Somente quando a funo invocada do programa principal que o seu parmetro formal, n, actualizado com os valores concretos representados pelos parmetros actuais (x, n, z) no existindo,assim, qualquer ambiguidade no processo. Quando a funo invocada, o respectivo parmetro formal actualizado, e o controlo do programa passa do ponto de invocao para a funo, o respectivo cdigo executado e, quando terminado, o controlo retorna ao ponto de invocao com o valor calculado da funo armazenado na varivel factorial. Assim, com este tipo de cdigo, os nomes das funes so variveis que retornam os valores das funes. Ento, um subprograma funo pode apenas retornar um nico valor de cada vez que invocado. Trata-se do equivalente s funes unvocas da matemtica. Quando os nomes das funes forem variveis, os seus tipos tm de ser obrigatoriamente declarados. Existe, no entanto, um detalhe na linguagem Fortran, relativamente declarao de funes que importante referir. De facto, em certos casos especiais como, por exemplo, no uso do conceito de recursividade (uma funo recursiva a que se invoca

19

a si prpria; estudaremos este conceito mais tarde) necessrio declarar as funes de um modo diferente. Ele exemplificado, reescrevendo a funo factorial: function factorial (n) result (fact) implicit none integer, intent (in) :: n integer :: fact, i fact = 1 do i = 1, n fact = fact* i end do end function factorial Este cdigo difere do anterior na incluso do atributo result (fact), o qual significa que o valor da funo no retornado na varivel factorial (o nome da funo) mas na varivel fact. E foi esta varivel que foi agora declarada. O tipo do argumento de result, neste caso fact, define o tipo da funo. O nome da funo passou a ser apenas uma designao para a invocar (tal como nas subrotinas) e no declarado. Quer se utilize um cdigo ou outro a funo sempre invocada pelo seu nome. Um outro atributo que surge nos dois cdigos intent (in) para o argumento x. Esse atributo pretende realar que esse argumento destinado aos valores que so dados funo e sobre os quais ela vai operar. Se esse argumento for suposto no ser alterado no interior da funo (o que poder suceder por lapso) ento o atributo intent (in) garante a no alterao (o compilador emitir uma mensagem e no produzir o cdigo executvel, no caso de se tentar alterar o valor). Os atributos intent (out) e intent(inout) contemplam as outras situaes possveis. No entanto, esses atributos no podem ser atribuidos ao nome da funo, ao argumento de result (conforme o caso) ou s variveis locais. Para eles, apenas a declarao do tipo permitida. Repare-se que no programa fact invocmos por trs vezes a funo factorial para calcular o factorial de diferentes nmeros. Suponhamos que no tinhamos possibilidade de definir e escrever a funo factorial como um subprograma independente. Ento, a nica alternativa seria repetir, trs vezes, o cdigo para o respectivo clculo. E se tivssemos mil factoriais para calcular teramos de repetir mil vezes um cdigo igual! Vemos, assim, que a possibilidade de definir e escrever um determinado cdigo, como um subprograma independente, uma nica vez e invoc-lo tantas vezes quantas as necessrias, confere aos programas uma grande versatilidade, generalidade e estruturao. 11.2. Subrotinas As funes so subprogamas que, como vimos, retornam um nico valor de um tipo determinado. A situao mais geral, no entanto, a de subprogramas independentes que possam retornar vrios valores de tipos diferentes. Esses suprogramas designam-se por subrotinas e tm uma estrutura semelhante das funes, diferindo delas, essencialmente, pelo modo de invocao. Assim, a estrutura geral de uma subrotina : subroutine nome_da_subrotina ( lista_de_argumentos) implicit none declarao das variveis ( com os atributos intent, excepto para variveis locais)

20

comandos end subroutine nome_da_subrotina O nome da subrotina no uma varivel, logo no declarado. A subrotina , em geral, colocada aps o end program do programa principal. A partir deste, a subrotina invocada com o comando: call nome_da_subrotina (lista_de_argumentos) Relativamente, aos nomes discriminados na lista_de_argumentos da subrotina e do comando da sua invocao eles no tm de ser iguais, aplicando-se os mesmos princpios que vimos para as funes. No entanto, quer para as funes, quer para as subrotinas, o seu nmero tem de ser igual e tem de haver uma correspondncia exacta entre o seu significado e o seu tipo. Todos estes aspectos, foram analisados no programa do pH realizado nas aulas prticas. 11.3. Mdulos No programa para o clculo do pH realizado nas aulas prticas, a funo hidro(x) necessita dos valores de at, bt, ka e kw os quais so lidos no programa principal. claro que poderamos declar-los no programa principal e na funo e, ento, discrimin-los na lista de argumentos da funo. A interface entre a funo e o programa principal seria correctamente estabelecida desse modo. No entanto, como a funo hidro(x) , por sua vez, passada como argumento para a subrotina biss, sendo l invocada vrias vezes teramos, por cada invocao, de discriminar alm de x, os parmetros at, bt, ka e kw. Isso daria lugar possibilidade de cometer erros, alm de no ser prtico de um modo geral, pois quanto mais parmetros existirem mais discriminaes teremos de fazer na lista de argumentos dos subprograms e respectivas invocaes. Assim, conveniente que possamos estabelecer interfaces entre programas principais e subprogramas sem ser atravs dos argumentos dos subprogramas. Tal possvel atravs de um procedimento designado por mdulo: module nome_do_mdulo implicit none save declarao das variveis end module nome_do_modulo O comando save garante que todos os valores declarados no mdulo sero preservados entre referncias em diferentes procedimentos (funes e subrotinas). Deve ser sempre includo em qualquer mdulo que declare dados para serem partilhados. As variveis declaradas no mdulo passam a ser acessveis ao programa principal e a qualquer subprograma (isto passam a ser variveis globais) atravs da instruo: use nome_do_mdulo com a estrutura seguinte: program nome_do_programa use nome_do_mdulo

21

implicit none ......................................... end program nome_do_programa subroutine nome_da_subrotina (lista_de_argumentos) use nome_do_mdulo implicit none ........................................... end subroutine nome_da_subrotina function nome_da_funo (lista_de_argumentos) use nome_do_mdulo implicit none ............................................... end function nome_da_funo Os mdulos, para alm de tornarem possvel o acesso de um conjunto dados a diferentes unidades dum programa, tm ainda outros papis muito importante em Fortran. De facto, at agora colocmos os subprogramas (funes e subrotinas) aps o end do programa principal. Desse modo eles so ententidos pelo compilador como subprogramas externos. Sublinhmos que para que se estabeleam as interfaces exactas entre os subprogramas e o programa principal era obrigatrio que o nmero de argumentos, o seu significado e tipo tivessem uma correspondncia correcta nas definies dos subprogramas e respectivas invocaes. Sucede que quando os subprogamas so externos os compiladores no verificam, em geral, essa correspondncia podendo, ento, gerar cdigo executvel sem emisso de qualquer mensagem de erro. Ento, a nvel da execuo do programa podem obter-se resultados errados sendo muitas vezes difcil traar a sua origem. Diz-se que as interfaces so implcitas, isto , so implcitamente supostas serem verificadas e testadas pelo programador. Os subprogramas, em vez de serem colocados aps o programa principal, podem, no entanto, ser empacotados em mdulos do seguinte modo: module nome_do_mdulo implicit none declarao das variveis globais contains definies das funes e subrotinas por ordem arbitrria end module nome_do_modulo Esta a definio mais geral do procedimento module. O acesso ao mdulo , como vimos anteriormente, realizado atravs de: use nome_do_mdulo colocado imediatamente a seguir declarao da respectiva unidade que o pretenda usar, e antes de implicit none. Colocando os subprogramas em mdulos, as respectivas interfaces passam a ser explcitas, isto , so explcitamente verificadas pelo compilador o qual detectar eventuais incorreces. Evita-se, assim, a produo de cdigo executvel com erros

22

implcitos. Alm disso, o empacotamento dos subprogramas em mdulos torna, obviamente, a programao mais estruturada e mais simples de analisar. Adicionalmente, o conceito de mdulo permite o desenvolvimento de outros aspectos mais avanados da programao que referiremos mais tarde (ver bibliografia indicada). As funes e subrotinas includas em mdulos designam-se por procedimentos modulares (module procedures) 11.4. Passagem de subprogramas como parmetros de outros subprogramas Na subrotina bissect do problema do pH, um dos parmetros formais a funo sobre a qual se vai aplicar o mtodo das bisseces sucessivas para encontrar uma das razes. Se a subrotina e funo forem tratadas como externas, isto , colocadas aps o end do programa principal, ento obrigatrio declarar o subprograma como externo sempre que ele seja um parmetro formal de outro subprograma. Assim, como chammos funo hidro(x), no programa principal fomos obrigados a declarar:

real, external :: hidro para depois invocar a subrotina bissect: call bissect( hidro, ....) Na definio da subrotina: subroutine bissect( f, ....) real, external :: f . Se, no entanto, tivermos empacotado os subprogramas num mdulo, as interfaces so explcitas e, ento, a declarao real, external no programa principal tem de ser omitida, pois de contrrio o compilador informa-nos de que temos duplicao de declaraes e o cdigo executvel no produzido. No entanto, na definio do subprograma, sempre que tivermos um outro subprograma como argumento formal, obrigatrio estabelecer uma interface, no subprograma, com esse argumento. Tal realizado atravs do chamado bloco de interface, que no mais do que uma estrutura declarativa colocada na seco de declaraes. No caso anterior, em vez de real, external :: f, teramos: subroutine bissect( f,) .. interface function f (x) implicit none real :: f real, intent(in) :: x end function f

23

end interface . Repare-se que no bloco de interface apenas so mencionadas as declaraes da funo e dos seus argumentos, no o cdigo da funo ou declaraes de eventuais variveis locais. Se f fosse uma subrotina, o bloco de interface seria semelhante: subroutine bissect ( f, ...) .. interface subroutine f (argumentos) implicit none declarao do tipo dos argumentos, com os respectivos atributos intent end subroutine f end interface ......................................... Recorde-se que o nome da subrotina no declarado, pois no se trata de uma varivel. Tambm, se existirem variveis locais nos subprogramas, elas no so declaradas nos blocos de interface como j referimos. Os blocos de interface desempenham outros papis muito importantes em programao avanada, aos quais faremos referncia mais tarde (ver bibliografia indicada). 12. Arrays como argumentos de subprogramas Durante as aulas prticas estudmos algumas operaes sobre matrizes: soma, multiplicao, produto interno, etc. A nvel do programa principal definimos as dimenses e formas das matrizes e reservmos a memria necessria atravs de allocatable arrays a duas dimenses. Esses arrays foram passados para uma subrotina onde a respectiva operao era realizada. Assim, no caso da multiplicao, por exemplo: program matmult implicit none . real, dimension ( :, :) , allocatable :: a, b, c . read (*,*) m !nmero de linhas da matriz a read (*,*) l !nmero de colunas da matriz a e de linhas da matriz b read (*,*) n !nmero de colunas da matriz b allocate ( a(m,l), b(l,n), c(m,n) ) ........................................... call produto ( m, l, n, a, b, c ) ............................................ end program matmult

24

subroutine produto ( m, l, n, a, b, c ) implicit none integer, intent (in) :: m, l, n real, intent (in), dimension ( m, l ) :: a real, intent (in), dimension ( l, n ) :: b real, intent (out), dimension ( m, n ) :: c end subroutine produto Os argumentos formais da subrotina so as extenses das dimenses dos diferentes arrays e os prprios arrays. A declarao das formas dos arrays na subrotina esto totalmente explcitas e diz-se que se so arrays formais de forma-explcita (explicitshape dummy arrays). O Fortran 90 admite outra forma de declarar arrays formais, chamados arrays formais de forma-assumida (assumed-shape dummy arrays). Para utilizar essa forma os subprogramas (funes ou subrotinas) tm de ter, obrigatoriamente, interfaces explcitas. Para isso podem ser includos num mdulo ou estabelecer um bloco de interface a nvel do programa principal. Assim, se a subrotina anterior estiver num mdulo chamado prod, pode reescrever-se o programa: module prod contains subroutine produto ( a, b, c ) implicit none real, intent (in), dimension ( :, : ) :: a real, intent (in), dimension ( :, : ) :: b real, intent (out), dimension ( :, : ) :: c ! Aplicar funo transformacional para o produto das matrizes c = matmul ( a, b) end subroutine produto end module prod program matmult use prod implicit none . real, dimension ( :, :) , allocatable :: a, b, c . read (*,*) m !nmero de linhas da matriz a read (*,*) l !nmero de colunas da matriz a e de linhas da matriz b read (*,*) n !nmero de colunas da matriz b

25

allocate ( a(m,l), b(l,n), c(m,n) ) ........................................... call produto ( a, b, c ) ............................................ end program matmult Reparemos no modo de declarar os arrays da subrotina. No necessrio indicar explcitamente as extenses das dimenses (l, m, n), nem pass-las como parmetros. Assim, na invocao da subrotina, elas tambm no figuram como argumentos. Uma vez que a interface explcita (trata-se de uma subrotina contida num mdulo) o compilador verifica todos os detalhes e assume, efectivamente, as formas dos arrays (as quais so definidas a nvel do programa principal, por exemplo) que passam para a subrotina. Note-se, contudo, que o nmero de dimenses dos arrays so simbolicamente indicados de modo semelhante ao dos arrays alocveis. A utilizao de arrays formais de forma-assumida evita a passagem das extenses das respectivas dimenses como parmetros dos subprogramas, com a garantia de que as interfaces ficam perfeitamente explcitas pois os subprogramas esto contidos num mdulo. Em alguns trabalhos prticos utilizaram-se, nas subrotinas, os arrays formais de formaexplcita, uma vez que as matrizes foram manipuladas com ciclos iterativos com ndice e para eles necessrio saber as extenses dos arrays: m, l e n. Tal opo foi propositada, pois torna mais compreensvel as manipulaes dos arrays, o que importante sob o ponto de vista pedaggico. No entanto, o Fortran 90 dispe de uma srie de funes intrnsecas transformacionais (ver bibliografia indicada) que permitem a manipulao directa de matrizes. No ltimo cdigo utilizmos a funo transformacional matmul(matriz_A, matriz_B) que realiza o produto de duas matrizes. claro que se pressupe que as matrizes dos argumentos da funo tm a forma apropriada para serem multiplicadas. Neste caso, no necessrio passar como argumentos as extenses das dimenses, pois com uma interface explcita o compilador conhece toda a informao necessria para a invocao da funo transformacional. No entanto, se quisermos tratar as matrizes de forma-assumida com ciclos iterativos, pode recorrer-se a uma srie de funes implcitas do Fortran (ver bibliografia indicada) que retornam as caractersticas dos arrays. Por exemplo, a funo size (b, 2) retorna a extenso da 2 dimenso do array b. 13. Formatao No pargrafo 4., referimos que o segundo * no comando: write(*,*) lista_de_output ou o nico * no comando: print *, lista_de_output significa que a escrita dos objectos especificados na lista realizada em formato livre. A escrita direccionada de acordo com o tipo de objectos na lista (list-directed output). Em geral, pode esperar-se que se o objecto for uma constante tipo caracter ela escrita tal como est designada na lista_de_output e justificada esquerda. Se se tratar de nmeros inteiros ou reais, so escritos justificados direita em campos de um certo

26

nmero de espaos que dependem da preciso numrica (veremos a questo da preciso no pargrafo seguinte) e do compilador utilizados. Uma coisa certa: em formato livre nunca se obtem um alinhamento de escrita correcto, se pretendermos uma sada de dados em forma de tabela, por exemplo, com um determinado nmero de casas decimais, ttulos, etc. Para tal, teremos de substituir os referidos * nos comandos write ou print, por constantes tipo caracter (...) onde constem os descritores de formatao. Nas aulas prticas estudmos os mais frequentes: rA para caracteres rIw para inteiros rFw.d para reais em notao decimal rESw.d para reais em notao cientfica rLw para dados lgicos onde r, w e d so nmeros inteiros que desigam, respectivamente, o nmero de descritores iguais e seguidos (repetidor; nem sempre utilizvel), o nmero de espaos (campo) onde o objecto escrito e o nmero de casas decimais. Os caracteres so sempre justificados esquerda, os nmeros e valores lgicos sempre justificados direita. As letras dos descritores podem ser maisculas ou minsculas. Vejamos um exemplo: program formatos implicit none integer :: x real :: I, j , avog logical :: bom x = 20 I = 3.1416 j = 44.451 avog = 6.023e23 bom = .true. ! Queremos o inteiro escrito na 1 linha num campo de 4 espaos. ! Os reais I e j escritos na 2 linha, em campos de 8 espaos com duas casas decimais. ! O real avog escrito na 3 linha ,em notao cientfica, num campo de 11 espaos ! com duas casas decimais. ! O valor lgico escrito na 4 linha, num campo de 5 espaos. ! Os valores so antecedidos pelos nomes das respectivas variveis seguidos de = ! Uma formatao possvel : write(*, (1x, A, I4)) x =, x write(*, (1x, A, F8.2, A, F8.2)) I =, I, j =, j

27

write(*, (1x, a, ES11.2)) avog =, avog write(*, (1x, A, L5)) bom =, bom end program formatos Repare-se que no 2 formato temos dois descritores iguais e seguidos. Poderamos ter utilizado um repetidor: write(*, (1x, 2(A, F8.2) )) I =, I, j =, j A regra geral : por cada objecto a escrever deveremos ter um descritor de formato correspondente ao respective tipo do objecto. Se, porventura, o campo escolhido no for suficiente para escrever o objecto aparecero asteriscos (*) no seu lugar e teremos de modificar o nmero de espaos do campo para que os objectos sejam escritos correctamente. Em todos os formatos, antes dos descritores de cada tipo de objecto, temos os caracteres 1x. sempre conveniente antecedermos os descritores por esses caracteres pela seguinte razo. Quando o comando write interpretado o sistema constri, a nvel da memria, uma imagem da linha a escrever antes de a enviar para o perifrico de sada. Essa imagem designa-se por buffer e tem usualmente 133 caracteres de comprimento. O 1 caracter considerado como controlo de escrita e nunca impresso. Os restantes 132 caracteres contm os dados eventuais a serem escritos. O caracter de controlo especifica o espaamento vertical para as linhas. 1x, significa um espao em branco que, sob o ponto de vista de controlo de escrita, interpretado como escrever na prxima linha. Se no colocarmos o caracter de controlo o sistema, em geral, toma o 1 caracter da lista de objectos como controlo de escrita o que pode ter um efeito imprevisvel, dependendo do compilador. Quando se utiliza a escrita no formatada, o caracter de controlo sempre automaticamente considerado. A formatao em Fortran tem muitos mais detalhes (ver bibliografia indicada) que permitem grande versatilidade na apresentao dos resultados e que iremos explorando durante as aulas prticas. Relativamente formatao de leitura, os descritores de formatao a introduzir nos comandos read so os mesmos que vimos para os comandos write. A leitura formatada particularmente importante nos casos em que tenhamos de ler dados de um ficheiro existente (num disco, por exemplo) cujos valores tenham sido formatados durante a sua criao. 14. Representao de nmeros inteiros e reais. Preciso numrica. 14.1. Inteiros Os nmeros so codificados no sistema binrio nas clulas da memria, as quais tm um comprimento, expresso em bits, caracterstico de uma dada mquina. Assim, existe uma limitao na preciso numrica dos clculos, a qual de importncia crucial, principalmente em computao cientfica. Quando se declaram variveis reais ou inteiras atravs das instrues real e integer, o sistema computacional assume uma preciso por-defeito, a qual depende do processador (computador + compilador) utilizado.

28

O espao de memria, por-defeito, dedicada ao armazenamento de um inteiro varia de computador para computador, mas , em geral, de 4 bytes nos PCs actuais. O menor nmero inteiro que pode ser representado com n-bits 2n-1 e o maior inteiro +2n-1 1. Ou seja, para inteiros de 4-bytes (recorde-se que 1-byte tem 8-bits) , o menor e o maior valores inteiros possveis so 2147483648 e +2147483647 respectivamente. No entanto, a maioria dos compiladores de Fortran 90 podem suportar inteiros de outros comprimentos (1, 2 e 8-bytes, por exemplo) para alm do comprimento por-defeito. Os diferentes comprimentos designam-se por espcies (kinds) de inteiros. O processador atribui a cada espcie um nmero inteiro designado, a nvel da linguagem Fortran 90, por um parmetro kind. Valores tpicos deste parmetro so: 1, 2, 4 e 8 respectivamente para inteiros de 1, 2, 4 e 8-bytes. Assim, se quisermos escolher explcitamente a preciso dos inteiros deveremos indic-la na declarao das variveis: integer (kind = 1) :: x integer (kind = 2) :: y integer (kind = 4) :: z integer (kind = 8) :: c

se for o padro equivalente a integer :: z no disponvel nos PCs actuais, mas noutros computadores

Os valores do kind e a sua correspondncia podem no ser exactamente os mencionados. Dependem do tipo de computador, do compilador e da rpida evoluo do material informtico. Dada uma mquina e um compilador de Fortran 90, podemos, no entanto, explorar estes aspectos e tornar os programas gerais e portveis de uma mquina para outra. Isto extremamente importante em computao cientfica, pois os resultados devem ser coerentes e reprodutveis nos diferentes sistemas que utilizarmos. E, no esqueamos, que a credibilidade e concluses podem depender fortemente da preciso numrica utilizada. No que se refere a inteiros, o Fortran 90 dispe de funes intrnsecas que permitem saber o valor do parmetro kind e do intervalo (range) de valores possveis para uma dada espcie de inteiros (expresso em potncias de 10), bem como estabelecer a priori um intervalo de representao e determinar o respectivo valor do parmetro kind. Suponhamos que declaramos uma varivel inteira por-defeito e lhe atribumos um valor qualquer. Os respectivos valores de kind e range so determinados do seguinte modo: integer :: i i=4 write(*,*) kind(i), range(i) A funo kind(i) retorna o valor do parmetro kind, para o computador e compilador utilizados, referente espcie do inteiro i; a funo range(i) retorna o expoente da potncia de 10 que representa o intervalo de valores para a espcie. No caso de um PC actual e para a maioria dos compiladores (nem todos, sublinhe-se) os valores retornados por aquelas funes, para inteiros por-defeito, so respectivamente, 4 e 9. Quer dizer, o intervalo de representao, expresso em potncias de 10, de 109 a +109 o que significa que se trata de inteiros de 4-bytes, pois como vimos acima para esses inteiros o menor e o maior inteiros possveis so 2147483648 e +2147483647 respectivamente ( ~ -2 .109 e 2.109). Contudo, se experimentarmos o cdigo anterior noutro computador, ou com outro compilador, o valor do kind pode no ser 4, mas 3 por exemplo. Tudo depende do fabricante do sistema. Ser, ento, muito importante ter a possibilidade de estabelecer a priori o intervalo (range) de representao conveniente para um estudo concreto e obter

29

automaticamente o correspondente valor do parmetro kind para qualquer sistema computacional, sem necessidade de alterar o cdigo, no que diz respeito ao valor do kind, quando se mudar de sistema computacioanal. Os programas sero, deste modo, completamente portveis no que diz respeito preciso numrica. Para isso, podemos utilizar o seguinte cdigo: integer, parameter :: k_numero = selected_int_kind ( r = 9 ) integer ( kind = k_numero) :: x A funo intrnseca do Fortran 90, selected_int_kind (r = 9) retorna o valor do parmetro kind (para qualquer sistema computacional) correspondente ao range (r) pretendido (neste exemplo, inteiros de 4-bytes) e armazena-o na constante-nomeada, k_numero (o nome escolhido por ns). Essa constante , ento, atribuda a kind na declarao das variveis. No caso de o range pretendido no ser permitido no sistema computacional utilizado a funo selected_int_kind retorna o valor 1 e o compilador informa-nos da impossibilidade. Por exemplo, inteiros de 8-bytes no so permitidos na maioria dos PCs actuais, portanto se especificarmos um r = 10, a funo retornar muito provavelmente 1 com a respectiva mensagem do compilador. 14.2. Reais A representao dos nmeros reais permite intervalos substancialmente mais largos do que a dos inteiros pois, como sabemos, realizada em termos da notao cientfica (mantissa + expoente de base 10). Mas aqui temos de considerar, para alm do range, a preciso (nmero de algarismos significativos). Tal como nos inteiros, quando se declara uma varivel real atravs da declarao: real :: m o sistema considera que se trata de uma definio por-defeito, para a qual so reservados, em geral, 4-bytes (tpico nos PCs actuais). No entanto, tambm aqui tudo dependente do fabricante. Reais de 4-bytes correspondem a uma preciso (precision) de cerca de 7 algarismos significativos, num intervalo (range) de cerca de 10-38 a 1038. Os reais por-defeito (em geral de 4-bytes, actualmente) desigam-se por reais de preciso simples (single precision). Embora o seu intervalo seja considervel, a preciso no em geral suficiente, particularmente em computao cientfica, de modo a evitar os erros de arredondamento (round-off errors). Assim, tambm possvel definir reais de outras espcies (kinds), como por exemplo os reais de 8-bytes, os quais so identificados, tal como no inteiros, por diferentes valores do parmetro kind. Os reais de 8-bytes que permitem uma preciso de cerca de 15 algarismos significativos num intervalo de cerca de 10-308 a 10308 designam-se, em geral, por reais de preciso dupla (double precision). Os valores do parmetro kind para estes e para os de 4-bytes so tpicamente 8 e 4 respectivamente, mas podem ser eventualmente diferentes conforme o fabricante do sistema. Para obviar esta situao, poderemos utilizar uma estratgia semelhante que seguimos para os inteiros, por exemplo: integer, parameter :: simples = selected_real_kind ( p = 6, r = 37 ) integer, parameter :: dupla = selected_real_kind ( p = 15, r = 308 )

30

real (kind = simples) :: m real (kind = dupla) :: n, s A funo intrnseca selected_real_kind tem agora os argumentos p, que especifica a preciso pretendida, e r, que especifica o intervalo requerido. Se porventura as especificaes no estiverem disponveis no sistema computcional utilizado, a funo retorna 1 se a preciso especificada no estiver disponvel, -2 se tal ocorrer para o intervalo requerido e 3 para a indisponibilidade de ambas, com a correspondente mensagem do compilador. Para os reais podemos tambm utilizar as funes kind (argumento) e range (argumento), e mais a funo precision (argumento) que retorna a preciso do respectivo argumento. Um bom princpio de programao executar previamente programas em Fortran, no sistema computacional disponvel, utilizando as funes referidas para inteiros e reais. Deste modo, ficaremos a saber quais as possibilidades e limitaes numricas do sistema. Para tornar os programas portveis, utilizar os cdigos anteriores. 15. Clculos aritmticos 15.1. Aritmtica inteira A aritmtica computacional inteira envolve somente dados declarados como inteiros e produz sempre um resultado inteiro. muito importante ter presente esta regra, especialmente quando uma expresso envolve divises, uma vez que no poder haver parte decimal no resultado. Se a diviso de dois inteiros no for intrinsecamente um inteiro, ento o computador automaticamente elimina a parte decimal. Isto pode dar lugar a surpresas, a respostas inesperadas e erradas! Vejamos alguns resultados com aritmtica computacional inteira: 3/4 = 0; 4/4 = 1; 5/4 =1; 6/4 = 1; 7/4 = 1; 8/4 = 2; 9/4 = 2 Perante estes resultados, a concluso bvia: nunca utilizar aritmtica computacional inteira em clculos intrinsecamente reais. Usar inteiros somente para objectos que sejam intrinsecamente inteiros: ndices de arrays e de ciclos iterativos, por exemplo. 15.2. Aritmtica Real A aritmtica computacional real envolve somente dados declarados como reais e produz sempre um resultado real. Assim, as operaes anteriores, realizadas no campo real, conduziro a: 3.0/4.0 = 0.75; 4.0/4.0 = 1.0; 5.0/4.0 =1.25; 6.0/4.0 = 1.50; 7.0/4.0 = 1.75; 8.0/4.0 = 2.0; 9.0/4.0 = 2.25 Tudo parece correcto. No entanto, a aritmtica real tambm tem as suas peculiaridades! De facto, como vimos no pargrafo anterior a representao numrica num computador limitada e tem apenas um certo grau de preciso, devido ao comprimento finito das respectivas clulas de memria. Suponhamos a seguinte operao:

31

1.0/3.0 Na realidade, sabemos que o verdadeiro resultado 0.333333333..., isto , uma dzima infinita peridica. Se a operao for realizada num computador em preciso simples (~7 algarismos significativos, recorde-se) o resultado ser: 1.0/3.0 = 0.3333333 que est claramente truncado. Ento, como consequncia, a expresso: 3.0* (1.0/3.0) que teoricamente igual a 1.0, em alguns computadores poder ser diferente de 1.0. Assim, testes de igualdade lgica no campo real devem ser cuidadosamente realizados com referimos no pargrafo 11. 15.3. Aritmtica mista A aritmtica computacional mista envolve dados declarados como reais e inteiros produzindo sempre um resultado real. Uma operao aritmtica directa entre um inteiro e um real no computacionalmente possvel pois, como sabemos, inteiros e reais so codificados e armazenados de modos completamente diferentes. , contudo, admissvel escrever expresses envolvendo inteiros e reais, as quais se designam por expresses mistas. A aritmtica envolvida designa-se por aritmtica mista. Sucede que perante uma expresso mista o computador converte automaticamente os inteiros em reais, se um inteiro e um real participarem na mesma operao, e usa aritmtica real. Assim, a expresso mista 3.0/2 (o 2 convertido em 2.0) tem como resultado 1.5 tal como a expresso real 3.0/2.0. No entanto, a aritmtica real pode ser confusa e conduzir, tambm, a resultados inesperados. Vejamos: A expresso mista 1.0 + 1/4 tem como resultado 1.0; A expresso mista 1 + 1.0/4 tem como resultado 1.25. Expresses teoricamente iguais com resultados computacionais diferentes! No 1 caso, a operao diviso no mista, portanto no h converso. O mesmo no se passa com o 2 caso. No entanto, em ambos os casos, a adio mista. A converso automtica tambm ocorre quando a varivel qual se atribui uma expresso de tipo diferente do resultado da expresso. Por exemplo, na atribuio: nres = 1.25 + 9/4 onde nres foi declarada como inteira, a expresso direita tem como resultado o nmero real 3.25. Mas como nres inteira, 3.25 automaticamente convertido a 3 antes de ser armazenado em nres. Assim, expresses computacionais mistas so perigosas e devem ser evitadas tanto quanto possvel. O Fortran dispe de funes intnsecas (ver bibliografia indicada) que foram a converso de inteiros em reais e vice-versa. Por exemplo, a funo real (argumento_inteiro) converte o valor do argumento_inteiro em real.

32

Neste contexto, existe uma situao que deve ser destacada: o caso da exponenciao. Consideremos a atribuio: resultado = y ** n em que resultado e y so reais e n inteiro. A expresso y**n significa usar y como um factor n vezes e isto o que o computador faz. Como y real e multiplicado por si prprio n vezes, o computador realizar naturalmente aritmtica real e no aritmtica mista. Consideremos agora a atribuio: resultado = y ** x onde resultado, y e x so reais. A expresso y**x significa usar y como factor x vezes. Mas neste caso x real, por exemplo, 3.6. Ora no fsicamente possvel multiplicar directamente y por si prprio 3.6 vezes! O computador ter, ento, de recorrer a um mtodo indirecto para calcular a expresso. O mtodo mais usual usar a frmula bem conhecida:
y x = exp(x ln y )

onde ln o logaritmo natural e exp a funo exponencial. Sucede que se y for um nmero negativo, por exemplo na expresso (2.0)**2.0, o computador produzir um erro de execuo, pois a funo logaritmo no definida para nmeros negativos. No entanto, elevar um nmero negativo a um expoente inteiro, por exemplo (-2) ** 2, perfeitamente correcto. Assim, devem usar-se expoentes inteiros sempre que possvel. 16. Comentrios finais Como dissmos no incio, o presente texto a sistematizao de uma parte das aulas tericas e apenas aborda alguns tpicos mais elementares do Fortran 90, no dispensando a consulta de outra bibliografia. Aspectos mais avanados como, por exemplo, o tratamento generalizado de arrays, variveis complexas, caracteres, apontadores, tipos derivados, blocos de interface, operadores, mdulos, ficheiros externos e internos, funes intrnsecas bem como especificaes e atributos mais completos de determinados comandos e unidades no foram abordados nestas notas, embora alguns deles tenham sido referidos brevemente nas aulas prticas. Tratam-se de aspectos muito importantes para o desenvolvimento de uma programao eficiente e clara, crucial em computao cientfica. Alm disso, pavimentam o caminho para verses mais actuais do Fortran, as quais facilitam a sua implementao em mquinas massivamente paralelas e permitem a abordagem da programao orientada por objectos. Bibliografia Fortran 90 for Engineers and Scientists Larry R. Nyhoff and Sanford C. Leestma, Prentice Hall, New Jersey, 1997.

33

Potrebbero piacerti anche