Escolar Documentos
Profissional Documentos
Cultura Documentos
Dezembro 1999
PREFCIO Comecei a programar em Fortran em 1977 ainda no curso de graduao em Engenharia Mecnica. O computador no caso tratava-se de um IBM-1130 com 8k de memria RAM. Desde ento tenho programado em Fortran. As pessoas no Brasil que programam usando Fortran em geral ainda praticam o padro 77. Este texto procura dar uma viso geral do atual padro da linguagem Fortran, o padro 90 j amplamente difundido nos compiladores disponveis dos principais fabricantes, tal como Microsoft, Lahey e Digital, bem como do padro 95 ainda pouco difundido.
2 9 9 9
10 10 10 10 11 11 11 12 12 13
14
14 14 14 15 15 15 16 16 17 17 17 18 18
2.5 Declarao de atribuio e clculos aritmticos 2.5.1 Aritmtica inteira 2.5.2 Aritmtica real 2.5.3. Hierarquia das operaes 2.5.4 Aritmtica mista 2.5.5. Aritmtica mista e exponenciao 2.6 Declarao de atribuio e clculos lgicos 2.6.1 Operadores relacionais lgicos 2.6.2 Operadores combinatoriais lgicos 2.7 Declarao de atribuio e variveis do tipo caracter 2.7.1 Sub-especificao de variveis do tipo caracter 2.7.2 Operador de concatenao 2.7.3. Operadores relacionais lgicos com variveis do tipo caracter 2.8 Funes intrnsecas 2.9 Declaraes de entrada e sada de dados 2.10 Inicializao de variveis 2.11 Declarao IMPLICIT NONE
18 19 19 19 20 20 21 21 22 22 23 23 23 24 25 25 26
27
27 27 27 28 29 29 30 30 31 31 32
33 34
CAPTULO 5 - ARRAYS
5.1 Declarando arrays 5.2 Usando elementos de array em declaraes Fortran 5.2.1 Usando elementos de array em declaraes Fortran 5.2.2 Inicializando elementos de array 5.2.3 Modificando a faixa de subscritos de um array 5.2.4 Subscritos de array fora da faixa de definio 5.2.5 O uso de constantes com declarao de arrays 5.3 Usando arrays completos e sub-conjuntos de arrays
44
44 45 45 45 46 46 47 47
5.3.1 Operaes com arrays completos 5.3.2 Definindo e operando com subconjuntos de arrays 5.3.3 Reformatando arrays 5.4 Usando funes intrnsecas Fortran com arrays 5.4.1 Funes intrnsecas elementares 5.4.2 Funes intrnsecas de questionamento 5.4.3 Funes intrnsecas de transformao 5.5 Atribuio de arrays com mscara: a estrutura WHERE 5.5.1 A estrutura WHERE do Fortran 90 5.5.2 A estrutura WHERE do Fortran 95 5.5.3 A declarao WHERE 5.6. A estrutura FORALL 5.6.1 A forma da estrutura FORALL 5.7. Arrays alocveis
47 49 50 51 51 51 52 53 53 53 54 54 54 54
57
58 60 62 64 65 65 66 67 68 69 71 72 72 73
6.8 Passando subprogramas como argumentos para outros subprogramas 6.8.1 Passando funes como argumento
73 73
75
75 75 77 78 78 78 79 79 79 80
9.1.1 Declarao de atribuio de ponteiro 9.1.2 Situao de um associao ponteiro-alvo 9.2 Usando ponteiros em declaraes de atribuio 9.3 Alocao dinmica de memria usando ponteiros 9.4 Usando ponteiros com estruturas derivadas de dados
REFERNCIAS
128
Unidade de controle
Memria secundria
Figura 1.1- Esquema de um computador. 1.1.1 - A CPU A parte principal do computador a Unidade de Processamento Central (CPU-Central Processing Unit). Ela constituda pela Unidade de Controle, que controle as outras partes do computador; pela Unidade de Lgica Aritmtica (ALU-Arithmetic Logic Unit); e pelas memrias internas (tambm conhecidas como registros). A CPU interpreta as instrues dos programas; pega os dados nas unidades de entrada de dados ou nas memrias principal e secundria e armazena-os nos registros; efetuas as instrues aritmticas na ALU e armazena o resultado nos registros. Depois
10
Manipulando-se a quantidade de bytes que se utiliza e o tipo de uso feito destes bytes em uma palavra pode-se armazenar na memria outros tipo de dados que no apenas inteiros. Os tipo comuns de dados que se pode armazenar so: inteiros, reais, alfanumricos, complexos e lgicos. No momento trataremos apenas dos inteiros, reais e alfanumricos. Existe tambm os tipos de dados derivados a partir destes cinco tipos fundamentais, mais adiante voltaremos aos tipos derivados ou especiais.
1.2.2.1. Dados inteiros
O tipo de dado inteiro consiste de uma quantidade de inteiros positivos, uma de inteiros negativos e o zero. O tamanho da palavra para armazenar inteiros varia de processador a processador (processador = computador + sistema operacional + compilador), mas os tamanho mais comuns so palavras de 1, 2, 4 e 8 bytes ou 8, 16, 32 e 64 bits. Em geral o maior e o menor inteiros representados so fornecidos por: maior inteiro = 2n-1-1, e menor inteiro = -2n-1, onde n a quantidade de bits da palavra.
1.2.2.2. Dados reais
Os nmeros inteiros no conseguem representar nmeros racionais ou irracionais. Tambm no representam nmeros muito grandes ou muito pequenos. Para contornar estas duas deficincias de representao de nmeros no inteiros na forma inteira, os processadores incluem o tipo de dado real ou floating-point. O tipo real armazena dados em um forma de notao cientfica. As duas partes de um nmero expresso em notao cientfica so chamados de mantissa e expoente. Por exemplo no nmero 5.7651010, a mantissa 5.765 e o expoente 8. No computador o sistema semelhante, apenas a base numrica binria ao invs de decimal. Os nmeros reais mais comuns nos processadores o real de 4 bytes ou 32 bits, dividido em duas partes, a mantissa com 24 bits e o expoente com 8 bits. A mantissa contm um nmero entre 1 e +1 e o expoente contm uma potncia de 2 suficientes para recuperar o valor do nmero representado, como segue
11
Os dados do tipo real so caracterizados por dois indicadores: a preciso e a faixa. Preciso a quantidade dgitos significativos que so preservados em um nmero, e faixa a diferena entre o maior e o menor nmero que pode ser representado. A preciso depende do nmero de bits na mantissa e a faixa depende do nmero de bits no expoente. Uma mantissa com 24 bits pode armazenas nmeros de -223 e +223, aproximadamente. Convertendo para a base decimal tem nmeros de 107 a +107, ou seja com este tipo de representao a preciso que se obtm de aproximadamente 7 dgitos decimais. Um expoente com 8 bits pode representar nmeros entre 2-128 e 2128, ou seja entre 10-38 e 10+38. Note que o tipo real de dados pode representar nmeros muito maiores ou muito menores que o tipo inteiro pode, porm no caso de usar 4 bytes tem-se apenas uma preciso de 7 dgitos decimais. Neste caso impossvel distinguir qualquer diferena entre os nmeros 1234567.1 e o 1234567.2. Isto ocorre porque quando um nmero com mais de 7 dgitos decimais armazenado em um tipo real com mantissa de 24 bits, o dgitos alm do stimo estaro perdidos para sempre. Este o erro de arredondamento (round-off error).
1.2.2.3. Dados alfanumricos
O tipo de dado alfanumrico (character) consiste de caracteres e smbolos. Um sistema tpico para representao de uma linguagem ocidental deve incluir os seguintes smbolos: 26 letras maisculas de A a Z; 26 letras minsculas de a a z; 10 nmeros 0 a 9; Smbolos variados, tais como: !@#$%^&*(){}[],<>?/~; Smbolos especiais requeridos pela linguagem, tais como: . Uma vez que o nmero de caracteres usando por uma linguagem ocidental no excede a 256 (28) pode-se utilizar um byte para armazenar qualquer caracter. Assim para armazenar 10 caracteres sero necessrio 10 bytes, e assim por diante. O conjunto de caracteres mais utilizado no mundo ocidental o ASCII (American Standard Code for Information Interchange). Existem outros sistemas semelhantes ao ASCII, tal como o BRASCII. Como algumas linguagens tal como o Japons e o Chins tm milhares de smbolos (aproximadamente 4000) faz-se necessrio um sistema com mais caracteres, assim surgiu o Unicode que armazena cada caracter em 2 bytes podendo assim abrigar 65536 (216) caracteres. Os primeiros 128 caracteres do Unicode so os mesmos do ASCII, ficando os blocos restantes designados para as diferentes linguagens: Japons, Chins, Portugus, Hebreu, rabe, etc.
1.3 Linguagens de computador.
Existem vrias camadas de linguagens entre o usurio e a mquina propriamente dita. Duas delas mais familiares ao usurio so o sistema operacional e o compilador. Vamos nos ater aqui ao compilador, mas no nos esqueamos que existem vrias outras camadas por trs at chegar mquina fsica propriamente dita. Isto necessrio porque a mquina s entende a linguagem dos fenmenos fsicos sobre os quais o computador baseia-se, enquanto os usurios humanos preferem uma
Verso preliminar. Escrito em 12/01/99. Impresso em 08/25/05
12
O Fortran o av de todas as linguagens de computao. O nome FORTRAN derivado de FORmula TRANslation. O Fortran foi criado originalmente na IBM entre 1954 e 1957. Este primeiro Fortran foi o Fortran I. Logo aps em 1958 a IBM lanou o Fortran II. A evoluo do Fortran continuou rapidamente at o aparecimento do Fortran IV em 1962. Esta verso do Fortran tinha muitas melhorias e tornou-se o standard da linguagem por 15 anos. Em 1966 o Fortran IV foi a primeira linguagem a ser padronizada pela ANSI (American National Standard Institute), sendo ento renomeado para Fortran 66. Em 1977 o padro Fortran tem uma nova grande melhoria com sintaxe e comandos que facilitavam a produo de programas estruturados. Este padro foi um sucesso e passou a ser conhecido como Fortran 77, em uso corrente at o momento. Novamente em 1990 o Fortran passa por uma grande mudana. O Fortran 90 um superconjunto do Fortran 77 e estende a linguagens em novas direes. Entre as novidades includas no Fortran 90 esto: livre formato do cdigo fonte, novas estruturas de arrays; operao com arrays completos; tipos de dados derivados; e interfaces explicitas. O Fortran 90 j suportado pelos principais compiladores disponveis no mercado (Microsoft, Lahey e Digital). Outro grande aspecto do Fortran 90 suportar processamento paralelo SIMD (Single Instruction Multiple Data). Em 1995 surgiu uma pequena atualizao do Fortran 90, o Fortran 95. Ainda no popular no Brasil (1999). Vamos nos dedicar prioritariamente ao Fortran 90, porm mostrando os aditivos feitos no Fortran 95.
Obsoleto
A forma fixa do cdigo fonte foi declarada obsolescente no Fortran 95, significando que candidata a ser deletada do padro nas futuras verses do Fortran.
13
Cada linguagem tem um conjunto de caracteres bsicos. Tal como o Chins e o Portugus, o Fortran 90 possui um conjunto bem definido de smbolos que podem ser usados. Outros smbolos no so permitidos e causar erro de sintaxe no programa. O alfabeto do Fortran 90 (Fortran character set) consiste de 86 smbolos mostrados na Tabela 2.1. Tabela 2.1 Conjunto de smbolos do Fortran 90. Quantidade de smbolos Descrio Smbolo 26 letras maisculas A-Z 26 letras minsculas a-z 10 nmeros 0-9 1 underscore _ 5 smbolos aritmticos +-*/** 17 smbolos variados ( ) . = , $ : ! % & ; < > ? branco
2.2 A estrutura de uma declarao Fortran 90.
Um programa consiste de uma seqncia de declaraes destinadas a atingir um determinado objetivo. As declaraes podem ser executveis e no-executveis. Declaraes executveis descrevem aes que o programa deve realizar quando executado. As declaraes no executveis provm informaes necessrias ao correto funcionamento do programa.
2.2.1 Formato livre do cdigo fonte.
At o Fortran 77 o formato do cdigo fonte era fixo, e ditado pelo antigo formato dos cartes perfurados. Quem j utilizou cartes perfurados sabe que tinha 80 colunas, as declaraes podiam ter no mximo 72 colunas, as declaraes deviam comear na stima coluna, e assim por diante. No formato livre atual no mais necessrio atender a estas restries. As declaraes podem comear na primeira coluna e cada linha pode ter at 132 caracteres. Se a declarao muito longa ento pode colocar o smbolo (&) e continuar a declarao na prxima linha. Uma declarao pode usar at 40 linhas consecutivas. No padro do Fortran 77 era praticamente obrigado o uso de labels, no Fortran 90 eles continuam existindo mas so praticamente desnecessrios. Qualquer coisa que venha aps o smbolo ! ser interpretado como comentrio pelo compilador. Este smbolo pode aparecer em qualquer coluna do programa e no apenas na primeira coluna, como era no padro Fortran 77.
14
Sempre use o cdigo fonte no formato livre quando escrevendo programas em Fortran 90/95.
O formato fixo foi declarado obsolescente no Fortran 95, significando que candidato a ser deletado nas prximas verses do Fortran. Assim no use o formato fixo em qualquer programa novo.
Como dito anteriormente um programa Fortran um conjunto de declaraes executveis e no executveis organizadas em uma ordem apropriada. Todo programa Fortran pode ser dividido em at trs sees: declarao, execuo e terminao. A seo de declarao consiste de um grupo de declaraes no-executveis, no comeo do programa, que define o nome do programa e as variveis, bem como seus tipos, que sero referenciados na seo de execuo. A seo de execuo consiste de um conjunto de declaraes executveis descrevendo as aes que o programa executar. A seo de terminao consiste de declaraes parando o processamento do programa e dizendo ao compilador onde o programa termina.
2.3.1 Estilo de programao
Talvez a principal conveno (no obrigatria) de estilo em Fortran 90 seja usar letras maisculas para as palavras-chave, tais como: PROGRAM, READ, WRITE; e minsculas para todo o restante. O Fortran case insensitive, isto , no diferencia entre letras maisculas e minsculas. Como os compiladores atuais mostram na tela as palavras-chave em cor diferenciado do restante, vou usar preferencialmente letras minsculas nos programas.
Boa Prtica
Na Figura 2.1 apresenta-se um primeiro programa em Fortran 90 que contm os principais elementos de todo programa Fortran 90.
!programador: joo batista aparecido !data: 03 de dezembro de 1999 program meu_primeiro_programa !Objetivo: !Mostrar alguns aspectos bsicos de um programa Fortran 90. !Somar dois nmeros reais x e y, colocar em uma varivel w, e imprimir. !declarao de variveis
15
Uma constante em Fortran um dado que definido antes do programa rodar, ou seja durante a compilao, e no ser alterado durante a execuo do programa. Uma varivel em Fortran um dado que podem mudar durante a execuo do programa. O valor de uma varivel Fortran pode ser ou no ser inicializado antes do programa ser executado. Quando o compilador Fortran encontra uma constante ou uma varivel ele reserva uma posio de memria para armazen-la. Uma varivel ou constante em Fortran possui nome nico e duas destas grandezas no podem ter o mesmo nome.
Use nomes de variveis com algum significado sempre que possvel. Crie um dicionrio de dados (variveis e constantes) para cada programa, visando facilitar a manuteno.
Conforme discutido anteriormente o tamanho (kind)de uma constante ou varivel inteira depende da quantidade de bits utilizado para armazenar a informao. Para declarar que uma varivel ou constante inteira numero_alunos procede-se da seguinte maneira
integer :: numero_alunos numero_alunos = 30
16
Pode existir variveis e constantes de todos os tipos que o compilador suportar. Por exemplo, variveis do tipo real podem ter diferentes comprimentos (kinds), mostrados na Tabela 2.2. Tabela 2.2-Preciso e faixa de nmeros reais em vrios computadores. nmero de Computador nmero total nmero de preciso de bits bits na decimal bits do mantissa expoente 32 24 7 8 VAX 64 56 15 8 32 24 7 8 IBM PC 64 53 15 11 32 24 7 8 Sun Sparc 64 53 15 11 Cray 64 49 14 15 faixa expoente do
10-38 a 1038 10-38 a 1038 10-38 a 1038 10-38 a 1038 10-38 a 1038 10-308 a 10308 10-2465 a 102465
O Fortran 90 suporta variveis lgicas, as quais s podem assumir dois valores TRUE ou FALSE. A maneira de declarar o tipo e atribuir valores a uma varivel lgica
logical :: w w = .true.
ou
logical :: w = .true.
O tipo de dados character consiste de grupos de smbolos alfanumricos. Pode-se declara o tipo e atribuir valores a uma constante ou varivel caracter das seguintes maneiras
character (len = 14) :: x, y, w character :: r,s character(12) :: t,q x = Joseh da Silva ! variaveis com 14 caracteres ! variaveis com 1 caracter ! variaveis com 12 caracteres
17
Se no for definido o tipo de uma variveis, por default ela ser inteira se comear com I,J,K,L,M,N e real no restante dos casos. Este default pode ser alterado usando o comando IMPLICIT. O mais adequado usar nas declaraes iniciais de um programa a declarao implicit none que obrigar o programador a definir todas as variveis sendo utilizadas, caso contrrio o compilador acusar o erro.
2.4.6. Mantendo as constantes consistentes em um programa
Evite ficar definindo uma mesma constante em diferentes posies do programa com diferentes precises. Por exemplo, suponhamos que existe uma constante chamada um_terco e que vale um tero, assim evite num local definir um_terco = 0.33 e em outro um_terco = 0.33333.
Boa Prtica
Mantenha as constantes precisas e consistentes em todo o programa. Para melhorar a consistncia e o entendimento do cdigo, d um nome cada constante importante e as use pelo nome em todo o programa.
por exemplo k = k + 1. Os operadores aritmticos do Fortran 90 so + * / ** adio subtrao multiplicao diviso exponenciao
Os operadores aritmticos mencionados acima so operadores binrios significando que devem aparecer entre duas variveis tal como
a + b, a b, a*b, a/b, a**b,
adicionalmente os smbolos + e podem ocorrer como operadores unrios, significando que podem ser aplicados a apenas uma varivel, como segue
18
2.5.1 Aritmtica inteira Aritmtica inteira aquela realizada apenas com nmeros inteiros. Aritmtica com inteiros sempre produz resultados inteiros. Esta regra importante principalmente quando existe diviso de dois inteiros, o resultados no poder ser fracionrio. Por exemplo
1/3 = 0, 2/3 = 0, 3/3 = 1, 4/3 = 1, 5/3 = 1, 6/3 = 2.
Cuidado
2.5.2 Aritmtica real
Preste ateno na aritmtica com inteiros. Diviso de inteiros sempre fornece resultados inesperados.
Aritmtica real ou aritmtica de ponto flutuante (floating-point arithmetic) aquela envolvendo constantes e variveis reais. A aritmtica real produz resultados reais que geralmente espera-se. Por exemplo
1./3. = 0.333333, 2./3. = 0.666666, 3./3. = 1., 4./3. = 1.333333.
Cuidado
Preste ateno na aritmtica com inteiros. Devido preciso limitada, duas expresses teoricamente idnticas podem freqentemente fornecer resultados ligeiramente diferentes.
Freqentemente vrias operaes aritmticas ocorrem combinadas em apenas uma expresso. Para remover qualquer ambigidade que poderia ocorrer se a ordem dos operadores fosse alterada, Fortran 90 estabelece uma srie de regras que governam a ordem que as operaes so realizadas em uma expresso. Estas regras so semelhantes s da lgebra. As operaes so realizadas na seguinte ordem de prioridade 1) O contedo de todos os parnteses so calculados, partindo do mais interno para fora; 2) Todas as exponenciais so calculadas, operando da direita para a esquerda; 3) Todas as multiplicaes e divises so efetuadas, operando da esquerda para a direita; 4) Todas as adies e subtraes so avaliadas, operando da esquerda para a direita.
Boa Prtica
Use parnteses o suficiente fazer que as equaes fiquem claras e simples para entender.
19
Quando operaes aritmticas so realizadas utilizando nmeros reais e nmeros inteiros, ento diz-se que uma aritmtica mista. Quando somamos dois nmeros inteiros o resultado ser tambm um inteiro. Quando somamos dois nmeros reais o resultado tambm ser um nmero real. Mas o que acontecer quando, por exemplo, somamos um real e um inteiro? Alm do mais existem muitas outras combinaes (em realidade infinitas) onde pode aparecer reais e inteiros juntos. No caso que propus acima o que vai acontecer que o programa vai perceber que se est tentando somar um inteiro e um real, ento no processo de soma o valor inteiro transformado para real (note a varivel continua inteiro) e somado com o outro real, resultando em um nmero real. Veja abaixo trs exemplos de diferentes tipos de operaes Tipo de expresso Operao Resultado Tipo de resultado Expresso inteira 5/2 2 inteiro Expresso real 5./2. 2.5 real Expresso mista 5./2 2.5 real necessrio muito cuidado em expresses mistas, principalmente onde exista diviso. Seja o seguinte exemplo
2.0 + 1/3 + 1/5 + 1/7 = 2.0
As divises na expresso acima tem prioridade sobre a soma, os termos de cada diviso tem apenas inteiros, ento uma aritmtica inteira, e o resultado das trs divises na expresso sero zero (inteiro), o qual ser ento transformado para zero (real) e adicionado com o 2.0, com resultado final igual a 2.0. Uma soluo para este problema transformar (durante a operao) todos os valores inteiros para reais. Veremos como fazer isto mais adiante.
Cuidado
Expresses do tipo misto so perigosas por que so difceis de entender e podem produzir resultados enganosos. Evite-as sempre que possvel.
Existe uma situao onde a aritmtica mista desejvel, na exponenciao. Se temos uma expresso
w = y**n
onde y um real negativo e n um inteiro. Os compiladores atuais interpretam esta expresso como: tome o valor y e multiplique-o por si mesmo n vezes. Por outro lado se temos a seguinte expresso
w = y**x
onde y real negativo e x tambm real, esta expresso ser calculada internamente no computador da seguinte forma w = exp(x*lny),
Verso preliminar. Escrito em 12/01/99. Impresso em 08/25/05
20
Da mesma forma que clculos aritmticos, clculos lgicos tambm so efetuados com declaraes de atribuio, que em geral tem a forma nome_da_variavel_logica = expresso_logica A expresso lgica do lado direito da expresso acima pode ser qualquer combinao de variveis lgicas, constantes lgicas e operadores lgicos. Um operador lgico um operador sobre dados numricos, caracter ou lgico, fornecendo um resultado final do tipo lgico.
2.6.1 Operadores relacionais lgicos
Operadores relacionais lgicos so operadores com dois nmeros ou caracteres que fornecem um resultado do tipo lgico (logical). O resultado depende da relao entre os dois valores, por isto so chamados operadores relacionais. A forma geral do operador relacional v1 operador v2 onde v1 e v2 so expresses aritmticas, variveis, constantes ou caracteres, e operador um dos operadores relacionais mostrado na Tabela 2.3 Tabela 2.3-Operadores relacionais lgicos Operao Novo estilo Estilo antigo Significado == .eq. igual a /= .ne. diferente > .gt. maior que >= .ge. maior ou igual a < .lt. menor que <= .le. menor ou igual a Se a relao entre v1 e v2 verdadeira o operador retorna .TRUE. como resultado, caso contrrio retornar .FALSE. Por exemplo 3>5 = .false.; 3<5 = .true.; B<D = .true.
21
Operadores combinatoriais lgicos so operadores com um ou dois operandos lgicos que fornecem um resultado lgico. Existem quatro operadores binrios: .and., .or., .eqv., e .neqv.; e um operador unrio, .not.. A forma geral de uma operao com combinatorial lgica com operador binrio w1.operador.w2 onde w1 e w2 so expresses lgicas, variveis ou constantes, e .operador. um dos operadores combinatoriais mostrados na Tabela 2.4. Tabela 2.4-Tabela da verdade para operadores combinatoriais lgicos. w1 w2 w1.and.w2 w1.or.w2 w1.eqv.w2 w1.neqv.w2 .false. .false. .false. .false. .true. .false. .false. .true. .false. .true. .false. .true. .true. .false. .false. .true. .false. .true. .true. .true. .true. .true. .true. .false. Na Tabela 2.5 apresenta-se a tabela da verdade para o operador unrio .not. Tabela 2.5- Tabela da verdade para o operador unrio .not. w1 .not.w1 .false. .true. .true. .false. Na hierarquia das operaes os operadores lgicos combinatoriais so calculados depois das operaes aritmticas e de todos os operadores lgicos relacionais. A ordem na qual os operadores em uma expresso so executados 1) Todos os operadores aritmticos so calculados de acordo com a seqncia apresentadas anteriormente; 2) Todos os operadores relacionais (==, /=, >, >=, <, <=) so calculados, operando da esquerda para a direita; 3) Todos os operadores .not. so executados; 4) Todos os operadores .and. so executados, operando da esquerda para a direita; 5) Todos os operadores .or. so executados, operando da esquerda para a direita; 6) Todos os operadores .eqv. e .neqv. so executados, operando da esquerda para a direita.
2.7 Declarao de atribuio e variveis do tipo caracter
Manipulaes de variveis do tipo caracter so efetuadas com a seguinte declarao de atribuio nome_da_variavel_caracter = expresso_com_variaveis_caracter. Um operador caracter (character operator) aquele que opera em dados que permitem um resultado do tipo caracter. Se o lado direito da expresso acima menor do que o da esquerda, algumas posies so ocupados com os caracteres atribudos e o restante preenchido com brancos. Porm se o lado direito maior do que o da esquerda, os caracteres em excesso sero descartados. Por exemplo, a declarao
Verso preliminar. Escrito em 12/01/99. Impresso em 08/25/05
22
Uma sub-especificao de uma varivel caracter seleciona uma parte da varivel e a trata como se fosse uma varivel independente. Uma sub-especificao definida colocando valores inteiros que representem o incio e o final dos caracteres selecionados, colocando-os entre parentes e separados por vrgula, logo aps o nome da varivel. Por exemplo se
a = abC4567 b = a(3:5)
ento b = C45.
2.7.2 Operador de concatenao
O operador (//) combina duas ou mais variveis caracter para formar ou outra. Por exemplo, a seguinte seqncia
a = abcdef b = 1234567 c = a(1:2)//b(3:4)//a(3:6)
Como j se disse anteriormente, existem certos padres normalizados de dados do tipo caracter, por exemplo o ASCII. Se um caracter igual ou diferente de outro fcil. No entanto difcil dizer se um caracter , por exemplo, maior que outro. A seguinte expresso a<A verdadeira ou falsa? Dentro do padro ASCII a expresso falsa, por que o A o elemento de nmero 65, e a o de nmero 97. Por esta conveno a maior que A. Isto pode levar a resultados surpreendentes se o usurio mover o seu programa de um local com padro para outro com outro padro. A seqncia de caracteres pode variar de forma catastrfica. Uma Segunda pergunta interessante : Como comparar variveis do tipo caracter com vrios caracteres. Por exemplo: BBBCFR maior ou menor que BBC? O algoritmo usado no compilador Fortran 90 compara o primeiro caracter de cada varivel, no caso so iguais, a compara-se os segundos caracteres, tambm so iguais; ento compara-se os terceiros caracteres que so diferentes, o C maior que o B, ento BBC>BBBCFR.
23
Cuidado
2.8 Funes intrnsecas
Existe um subconjunto das funes matemticas que associa a um ou mais argumentos um nico resultado. Nos clculos tcnicos e cientficos faz-se necessrio calcular diferentes tipos de funes. Estas funes podem muitas vezes ser escritas como combinao de outras funes mais bsicas. O compilador Fortran 90 apresenta um conjunto, aproximadamente 112, destas funes bsicas, chamadas funes intrnsecas (intrinsic functions). Caso o usurio no consiga reduzir sua funo de interesse a combinaes das funes intrnsecas, ser necessrio desenvolver uma nova, a partir dos fundamentos do clculo. Funes criadas pelo prprio usurio podem ser definidas como funes externas (external function)ou funes internas (internal function). Retornaremos a este assunto mais adiante. Abaixo, na Tabela 2.6, apresenta-se algumas das funes intrnsecas suportadas pelo Fortran 90/95. Tabela 2.6 Vinte funes intrnsecas suportadas pelo Fortran 90/95. Funo e Definio Tipo de Tipo de Comentrios argumentos matemtica argumento resultado sqrt(x) real real raiz quadrada de x x abs(x) |x| real/inteiro real/inteiro valor absoluto de x achar(i) inteiro char(1) retorna caracter ASCII sin(x) sin(x) real real seno de x cos(x) cos(x) real real co-seno de x tan(x) tan(x) real real tangente de x real real exponencial de x exp(x) ex real real logaritmo natural de x log(x) loge(x) real real logaritmo base 10 de x log10(x) log10(x) iachar(c) char(1) inteiro posio de c no ASCII int(x) real inteiro parte inteira de x nint(x) real inteiro inteiro prximo de x real(i) inteiro real converte inteiro para real mod(a,b) a-p*int(a/p) real/inteiro real/inteiro resto de a/b max(a,b) real/inteiro real/inteiro pega o maior de a e b min(a,b) real/inteiro real/inteiro pega o menor de a e b -1 real real inverso do seno de x asin(x) sin (x) real real inverso do co-seno de x acos(x) cos-1(x) real real inverso da tangente de x atan(x) tan-1(x) Note na tabela acima tem uma funo para converter inteiros em reais, evitando assim os inconvenientes da aritmtica mista. Esta funo a real(i). Assim a expresso
2.0 + 1/5 = 2.0
24
Uma declarao de entrada de dados l um ou mais dados de um equipamento de entrada de dados. Da mesma forma uma declarao de sada de dados escreve um ou mais dados em um equipamento de sada de dados. Uma forma simplificada de declarao de entrada de dados read(*,*) lista_de_variaveis. Analogamente, uma declarao de sada de dados write(*,*) lista_de_variaveis. Abaixo apresenta-se um exemplo que l duas variveis inteiras e imprime as duas inteiras e mais duas reais j definidas no programa
program entrada_saida integer :: i,j real :: p=1.0, q=7.0 write(*,*)'digite i e j' read(*,*)i,j write(*,*)i,j,p,q stop end entrada_saida
Boa Prtica
2.10 Inicializao de variveis
Imprima na tela, quando possvel, todas as variveis que so lidas por um programa, para certificar que foram digitadas e processadas corretamente.
Uma vez que as variveis m e x no foram inicializadas, quando so impressas podem conter qualquer valor que represente o estado dos bits daquelas posies de memria que o compilador lhes atribuiu. Alguns compiladores zeram as variveis no inicializadas. Em todo o caso os resultados podem ser catastrficos.
Boa Prtica
25
Uma declarao no executvel de muita utilidade a implicit none. Quando utiliza-se implicit none todos os defaults de digitao so desabilitados e o programador ter obrigatoriamente que definir todas as variveis utilizadas. Se algumas varivel foi digitadas errada, quando da compilao o compilador emitir um erro indicando que existe uma varivel no declarada. A declarao implicit none dever aparecer logo aps a declarao program e antes das declaraes de tipos de variveis.
Boa Prtica
Defina sempre e explicitamente todas as variveis do programa e use a declarao implicit none para ajudar a localizar variveis no declaradas e variveis digitadas incorretamente.
26
Como estabelecido acima as bifurcaes selecionam pores de cdigo que sero executadas enquanto salta outras pores que no sero executadas. Basicamente tem-se algumas variantes da declarao IF, e a declarao SELECT CASE.
3.1.1. Estrutura do tipo bloco IF
A forma mais comum de declarao IF o bloco IF. Esta estrutura define que um determinado bloco de cdigo s ser executado se uma determinada expresso lgica for verdadeira. A forma do bloco IF
if (expressao_logica) then declaracoes_executaveis end if
Boa Prtica
Sempre indente o corpo de um bloco IF de alguns espaos para melhorar a leitura do cdigo.
Na estrutura bloco IF mostrada acima uma poro de programa executada se uma expresso lgica verdadeira. Se for falsa o bloco de declaraes simplesmente pulado. No entanto existem situaes onde deseja-se executar um bloco de declaraes se a expresso lgica for verdadeira, e um outro bloco se a expresso lgica for falsa. Uma declarao ELSE seguida de nenhuma, uma ou mais declaraes ELSE IF podem ser adicionadas ao bloco IF, para satisfazer este tipo de necessidade. O bloco IF com declaraes ELSE e ELSE IF tem a seguinte forma
if (expressao_logica_1) then declaracoes_executaveis_1 else if (expressao_logica_2) then declaracoes_executaveis_2 else declaracoes_executaveis_3 end if
27
Todas as formas de declarao IF podem ser nomeadas. Isto pode-se dar nomes a cada uma destas estruturas. A forma do bloco IF nomeada ser ento
{nome:} if (expressao_logica) then declaracoes_executaveis end if {nome}
Quando tem-se estruturas com muitos laos e bifurcaes indentados uns dentro dos outros, caso ao digitar seja esquecido alguma declarao end, o compilador acusar um erro que muitas vezes difcil de encontrar. A nomeao destas estruturas opcional mas pode ser usada para evitar ambigidades e facilitar a leitura e a manuteno do cdigo.
Boa Prtica
Destine um nome a todas as estruturas grandes e complicadas de IFs do programa, para facilitar a leitura e a manureno.
28
Quando a estrutura bloco IF tem apenas uma declarao executvel ento ela uma forma especial mais simplificada. Torna-se ento uma declarao de apenas uma linha, como segue
if(expressao_logica) declaracao_executavel
A estrutura CASE uma forma de implementar mltiplas bifurcaes simultneas. Permite ao programador escolher um determinado bloco de cdigo a ser executado, entre vrios, baseando em uma expresso envolvendo dados dos tipos inteiro, caracter e lgico. A forma geral da estrutura CASE
{nome:} select case (expressao_case) case (seletor_case_1) {nome} declaracoes_executaveis_1 case (seletor_case_2) {nome} declaracoes_executaveis_2 case (selector_case_3) {nome} declaracoes_case_3 case default {nome} declaracoes_default end select {nome}
Se a expressao_case estiver de acordo com o seletor_case_1 as declaracaoes_executaveis_1 sero executadas e o restante ser pulado. O mesmo acontece se a expressao_case estiver de acordo com o seletor_case_2 as declaracaoes_executaveis_2 sero executadas e o restante ser pulado. E assim por diante. Se a expressao_case no estiver de acordo com nenhum dos seletor_case, ento as declaracoes_default sero executadas. O case default opcional. Caso no exista o case default e a expressao_case no atenda nenhum dos seletor_case, ento nenhum dos blocos ser executado. Abaixo apresenta-se um exemplo de utilizao da declarao select case
! Objetivo: Demonstrar a utilidade da declarao CASE ! Distingue entre as palavras faca, garfo, e colher. character(6) :: x write(*,*)'Digite o talher!' read(*,*)x select case(x) case ('faca') write(*,*)'O talher uma faca' case ('garfo') write(*,*)'O talher um garfo' case ('colher') write(*,*)'O talher uma colher'
29
Laos so estruturas de controle que permitem a execuo de um bloco de cdigo vrias vezes em seqncia. As duas formas bsicas de laos so: laos com terminao indefinida, e laos com terminao bem definida. No existe um lao puro, uma vez que o mesmo se repetiria eternamente. Assim todo lao possui na sua estrutura (implcita ou explicitamente) uma bifurcao responsvel pela sada do lao. Nos laos de terminao indefinida, o lao continua indefinidamente at que uma da condio de controle seja atendida e completa-se a execuo do lao. Se no for bem projetado este tipo de lao pode continuar indefinidamente. Por outro lado o lao com terminao bem definida, executa uma quantidade finita de laos e termina sua execuo. Neste caso no h o risco da execuo continuar indefinidamente.
3.2.1. O lao com terminao indefinida
Como falado acima, este tipo de lao executa um bloco de declaraes indefinidamente at que uma determinada condio seja atingida. A forma geral desta declarao em Fortran 90/95
do declaracoes_executaveis_1 if(expressao_logica) exit declaracoes_executaveis_2 end do
30
O compilador do Fortran 90/95 tem uma forma alternativa, mais simples, de implementar a estrutura mostrada no item anterior. Este caso eqivale ao mostrado acima onde no se tenha nenhuma declarao no sub-bloco declaracoes_executaveis_1, assim esta implementao alternativa, chamada de do while tem a seguinte forma
do while (expressao_logica) declaracoes_executaveis end do
Nesta estrutura acima o bloco de declaracoes_executaveis ser executada indefinidamente, enquanto a expressao_logica for verdadeira. Neste tipo de estrutura tambm possvel que o lao no atinja terminao.
3.2.3. O lao com terminao bem definida
Este tipo de lao tem terminao garantida porque o lao ser executado uma quantidade bem definida de vez e ento terminar. s vezes este tipo de lao pode ser chamado tambm de lao iterativo ou lao com contagem. O lao com contagem construdo da seguinte forma
do index = i_start, i_end, i_incr declaracoes_executaveis end do
onde index uma varivel do tipo inteiro usada como um contador do lao. A variveis inteiras i_start, i_end e i_incr so parmetros do lao de contagem, elas controlam os valores assumidos por index durante a execuo do lao. O parmetro i_incr opcional e o seu valor default a unidade. Os valores assumidos por index comeam com o valor i_start, vai sendo acrescido de i_incr, at atingir o valor i_end, inclusive. O critrio de parada do lao em realidade index*i_incr i_end*i_incr, definido desta forma esta mais apto a tratar de casos patolgicos. Entretanto, testando o caso patolgico onde i_incr = 0, o compilador (Microsoft V4) no acusou erro e o programa quando rodou foi descontinuado por erro fatal. O nmero total de iteraes (i_iter) que ser executado em um lao de contagem pode ser obtido aproximadamente por i_iter = (i_end-i_start+i_incr)/i_incr. A varivel de controle index tem um escopo interno do lao, no devendo ser modificada (externamente) durante a execuo do lao. Pode ser inserido um comando IF (expressao_logica) EXIT no interior deste tipo de lao para provocar uma interrupo precoce quando a expressao_logica for verdadeira. Aps o trmino do lao DO o valor da varivel de controle index indefinido (dependendo de cada implementao de compilador). proibido fazer um desvio incondicional (go to) de fora para dentro de um lao DO. A seguir tem-se uma implementao deste tipo de lao
31
Boa Prtica
Sempre indente o corpo de um bloco DO de alguns espaos para melhorar a leitura do cdigo.
Nunca modifique o valor da varivel de controle de lao DO enquanto o lao est sendo executado. O uso de varivel real como varivel de controle de lao de DO foi declarado obsolescente no Fortran 90 e foi deletado do Fortran 95. Nunca dependa do valor de uma varivel de controle de lao de DO depois que o lao termina.
Boa Prtica
Pode-se utilizar adicionalmente as declaraes cicle e exit para controlar as operaes enquanto executa-se algum tipo de lao. Se a declarao cicle for usada dentro de um lao a execuo ser parada e o controle retornar ao comeo do lao. A varivel de controle ento ser incrementada e o processamento continuar. Abaixo mostra-se um exemplo de tal aplicao
program cycling integer :: i, m=10 do i=1,m,1 if(i==5) cycle write(*,*) i end do
32
Quando a declarao EXIT utilizada dentro de um lao de DO a execuo do lao ser interrompida e o controle da execuo ser passada para a primeira declarao executvel aps o lao. Adaptando-se o exemplo acima para testar o efeito da declarao EXIT tem-se
program exiting integer :: i, m=10 do i=1,m,1 if(i==5) exit write(*,*) i end do stop end program exiting 3.2.5 Laos nomeados
Da mesma maneira que as bifurcaes (IFs) podem ser nomeados, os laos (DOs) tambm podem. Os dois tipos principais de laos podem ser nomeados da seguinte maneira
{nome:} DO declaracoes_executaveis_1 if(expressao_logica_1) cycle {nome} declaracoes_executaveis_2 if(expressao_logica_2) exit {nome} declaracoes_executaveis_3 end do {nome} {nome:} DO index = i_start, i_end, i_incr declaracoes_executaveis_1 if(expressao_logica_1) cycle {nome} declaracoes_executaveis_2 if(expressao_logica_2) exit {nome} declaracoes_executaveis_3 end do {nome}
Boa Prtica
D nomes aos principais laos no programa para deixar claro quais declaraes executveis pertencem a um determinado lao.
33
Quando vrios laos esto um dentro do outro, diz-se que o laos esto aninhados ou que formam um ninho de laos. Abaixo tem-se um exemplo deste tipo de ninho de laos
{nome_2:} DO declaracoes_executaveis_1 {nome1:} DO declaracoes_executaveis_2 if(expressao_logica_1) cycle {nome_1} declaracoes_executaveis_3 if(expressao_logica_2) exit {nome_2} declaracoes_executaveis_4 end do {nome_1} declaracoes_executaveis_5 end do {nome_2}
As declaraes cycle e exit que aparecem acima tm que referir a qual lao a declarao se aplica quando existe mais de um lao. Se estas declaraes no tiverem referncias a qual lao se aplicam, ento se aplicaro ao lao externo mais imediato no sentido de dentro para fora.
Boa Prtica
Use declaraes CYCLE e EXIT nomeadas em ninho de laos para deixar claro que a declarao afete o lao correto.
34
Na Tabela 4.1 tm-se os tipos mais comuns de descritores de formato usados, juntamente com seus significados. Tabela 4.1-Simbolos usados com os descritores de formato. Smbolo Significado c Nmero da coluna d Nmero de dgitos direita do ponto decimal m Nmero mnimo de dgitos a ser apresentado n Nmero de espaos para pular r Nmero de vezes o formato repetido w Nmero de caracteres a ser usado
4.1.1 Sada de dados inteiros: O descritor I
O descritor usado para formatar a apresentao de inteiros o descritor I. Sua forma geral
r(Iw) ou r(Iw.m)
onde r, w e m tem os significados mostrados na Tabela 4.1. Abaixo tem-se um pequeno exemplo do uso da formatao para inteiros.
program formato_inteiro integer :: i=0, j=17, k=254, l=7689 write(*,1)i, j, k, l 1 format(4(1x,i3.3)) stop end program formato_inteiro
As estrelas que aparecem acima significam que o formato foi insuficiente para imprimir o dado.
35
Um dos descritores usados para formatar dados reais o F. Sua forma geral
r(Fw.d)
Muitas vezes dados do tipo real variam muito de faixa e torna-se difcil imprimi-los usando o descritor F. Um outro descritor para nmeros reais o descritor E, tambm conhecido como notao exponencial. Esta formatao consiste em imprimir um nmero normalizado entre 0 e 1, multiplicado por um potncia de 10. O formato geral do descritor E r(Ew.d) Alterando o programa anterior para usar a formatao E tem-se program formato_real_E real :: i=1.,j=1./3.,k=21.123456789,l=1234.567 write(*,1)i,j,k,l 1 format(4(1x,E14.7)) stop end program formato_real_E O resultado deste programa ser .1000000E+01 .3333333E+00 .2112346E+02 .1234567E+02
36
O descritor ES muito semelhante ao descritor E. Esta formatao consiste em imprimir um nmero normalizado entre 1 e 10 (lembre-se que no descritor E este nmero era entre 0 e 1), multiplicado por um potncia de 10. O formato geral do descritor ES r(ESw.d) Alterando o programa anterior para usar a formatao ES tem-se program formato_real_ES real :: i=1.,j=1./3.,k=21.123456789,l=1234.567 write(*,1)i,j,k,l 1 format(4(1x,ES14.7)) stop end program formato_real_ES O resultado deste programa ser 1.0000000E+00 3.33333330E-01 2.1123460E+01 1.2345670E+03
Boa Prtica
Quando imprimindo dados do tipo real muito grandes ou muito pequenos, utilize o descritor ES, porque idntico notao cientfica e os resultados podero ser entendidos mais facilmente pelo usurio.
O descritor L usado para formatar dados do tipo lgico, e sua forma geral r(Lw) O programa abaixo
program formato_logico_L logical :: x=.true., y=.false.
37
Dados do tipo caracter so formatados com o descritor A, que a seguinte forma geral
r(A) ou r(Aw)
Estes dois descritores operam quando os dados j esto no buffer. O descritor X insere brancos nos locais designados, e o T tabula a coluna para onde se deve ir. O descritor X tem o seguinte formato
38
Cuidado
Quando utilizando descritor T seja cuidadoso para no produzir resultados aparentemente falsos.
O descritor / (slash) quando encontrado em uma declarao format envia o contedo do buffer para a impressora e inicia um novo buffer, isto eqivale a dizer que o descritor / inicializa uma nova linha de impresso. Se colocarmos vrios slashes seguidos o efeito ser pular vrias linhas de impresso. No programa abaixo mostra-se o efeito causado pelo descritor slash
program formato_slash logical :: x=.true., y=.false. write(*,1)x,y 1 format(2(1x,L1//)) stop end program formato_slash
Cuidado
Em todos programas mostrados at o momento as entradas de dados foram pelo teclado e a sada na tela. A quantidade de dados de entrada e de sada eram pequenas. Freqentemente, necessita-se ler grandes arquivos armazenados em disco, e tambm gravar grandes arquivos em disco. Nos computadores esta entidade que armazena dados em disco (ou mesmo em fitas ou outros dispositivas de armazenamento de dados) conhecida como FILE. Todo file constitudo de um conjunto de linhas chamadas de RECORD. O Fortran capaz de ler ou escrever informaes em um file, um record por vez. Quando se l dados em um arquivo, um record de cada vez, em ordem consecutiva, diz-se que este o acesso seqencial. Este tipo de acesso mais usado em unidades de fita magntica. Quando salta-se livremente de um record para outro sem ordem especfica, diz-se que este um acesso direto. A maneira de direcionar uma declarao read ou write para um determinado dispositivo de entrada ou sada de dados feita colocando o nmero correspondente daquela unidade no lugar do primeiro asterisco que aparece nas declaraes read e write usadas nos programas mostrados anteriormente. Vrias declaraes do Fortran so destinadas a controle este processo de ler ou escrever dados em um arquivo (file), as discutidas neste captulo esto na Tabela 4.2. Tabela 4.2-Declaraes Fortran de entrada e sada de dados Declarao de entrada ou sada Funo Associa um arquivo em disco com um nmero de open entrada ou sada. Termina a associao iniciada com a declarao close open L dados de uma unidade de entrada de dados read especificada Escreve dados em uma unidade de sada de dados write especificada Move o ponteiro para o comeo do arquivo rewind Move o ponteiro um record em um arquivo backspace
Verso preliminar. Escrito em 12/01/99. Impresso em 08/25/05
40
A declarao open associa um arquivo (file) com um nmero dado de unidade de entrada ou sada de dados. Sua forma
open(lista_open)
onde lista_open contm uma srie de especificaes especificando a unidade de entrada ou sada de dados, o nome do arquivo, e informaes de como acessar o arquivo. As vrias especificaes dentro da lista_open so separadas por vrgulas. Veremos neste captulo algumas destas especificaes, outras apenas em outros captulos mais adiante. As cinco especificaes mais importantes so: 1) UNIT = expressao_inteira: Esta especificao indica qual o nmero inteiro que vai se usar para associar com arquivo. 2) FILE = expressao_caracter: Esta especificao estabelece o nome do arquivo a ser aberto. 3) STATUS = expressao_caracter: onde expressao_caracter pode assumir os seguintes valores: OLD, NEW, REPLACE, SCRATCH, e UNKNOWN. A expresso old refere-se a arquivos j existentes, new refere-se a arquivos novos, unknown refere-se tanto a arquivos antigos quanto novos, scratch refere-se a arquivos que sero deletados pelo sistema quando a execuo do programa terminar, e replace significa que um arquivo vai ser aberto para sada de dados, independente da existncia de outro arquivo com o mesmo nome. 4) ACTION = expressao_caracter: Esta especificao indica se o arquivo deve ser aberto apenas para leitura, apenas para escrita, ou para a leitura e escrita. Nestes trs casos a expressao_caracter assume os seguintes valores: READ, WRITE, e READWRITE. Os prprios ttulos so auto-explicativos. 5) IOSTAT = variavel_inteira: Esta especificao indica o nome de uma varivel inteira na qual o status da operao open ser retornado. Se a abertura do arquivo bem sucedida o valor zero (inteiro) ser retornado na variavel_inteira. Se o processo no for bem sucedido o sistema retornar em variavel_inteira um nmero positivo correspondente ao cdigo de mensagem de erro. Abaixo segue alguns fragmentos de cdigo mostrando alguns casos de declaraes open
integer :: i_error open (UNIT=8, FILE=exemplo.dat, STATUS=old, ACTION=read, IOSTAT=i_error)
integer :: i_error,unit character(len=5) :: file_name unit=25 file_name=saida open (UNIT=unit, FILE=file_name, STATUS=REPLACE,& ACTION=WRITE, IOSTAT=i_error)
Boa Prtica
Sempre tenha cuidado ao especificar os argumentos da declarao OPEN, dependendo se est lendo ou escrevendo no arquivo. Esta prtica vai prevenir erros tais como escrever em cima de um arquivo que deseja-se preservar.
41
A declarao CLOSE fecha o arquivo e libera o nmero de entrada e sada que estava associado com aquele arquivo. O formato desta declarao
close(lista_close)
onde a lista_close deve conter o nmero de entrada e sada associado ao arquivo que se deseja fechar. Pode existir nesta lista outros argumentos que sero discutidos em outro captulo. Um exemplo o que fecha o arquivo associado ao nmero de entrado e sada igual a oito, como segue
close(UNIT = 8) ou simplesmente close(8) 4.2.3 READs e WRITEs a arquivos em disco
Uma vez que um arquivo foi conectado a um nmero de entrada e sada de dados atravs de um declarao open ser possvel ler do arquivo ou escrever no arquivo utilizando as declaraes read e write j utilizadas anteriormente. Por exemplo o seguinte bloco de declaraes
real :: x,y,z open (UNIT=8, FILE=exemplo.dat, STATUS=OLD, IOSTAT=i_error) read (8,*) x,y,x
A especificao IOSTAT= pode ser usada juntamente com a declarao READ (ou mesmo com WRITE) quando se estiver operando com arquivos em disco. A declarao read ficar ento com o seguinte aspecto
integer :: i_err, i read(7,10,IOSTAT=i_err) i
Neste a declarao read tentar ler a varivel inteira i no arquivo que estiver vinculado ao nmero 7 de entrada e sada de dados. O nmero 10 o label da declarao format que especificar a formatao de leitura. Tudo isto j se fazia antes. Da forma anterior, quando a leitura era bem sucedida o programa continuava a execuo. Quando a leitura era malsucedida o programa descontinuava. Com a introduo deste novo argumento IOSTAT=i_err, quando a leitura bem sucedida o sistema ir retornar um valor zero para i_err, e o programa continuar. Se a leitura for malsucedida o programa no descontinuar e retornar um valor no nulo para i_err, utilizando um IF est situao pode ser detectada e alguma providncia tomada para resolver o problema ou fazer uma terminao elegante do programa ao invs das terminao selvagens que ocorre quando este recurso no usado.
42
Boa Prtica
Sempre inclua a especificao IOSTAT= quando lendo (ou mesmo escrevendo) um arquivo de disco. Isto prov um meio gracioso de detectar excepcionalidades em operaes com arquivos.
Conforme falado anteriormente um dos tipos de arquivos em Fortran so os seqenciais, que devem ser lidos ou escritos um record aps em uma ordem consecutiva. s vezes necessrio ler um mesmo dado duas vezes, ou escrever duas vezes na mesma posio. O Fortran apresenta duas declaraes para resolver este tipo de problema. O BACKSPACE que move o ponteiro um record para trs de cada vez, e o REWIND que reposiciona o ponteiro no incio do arquivo. As formas destas duas declaraes so como segue
backspace (UNIT=unit_number)
e
rewind (UNIT=unit_number)
onde unit_number o nmero da unidade de entrada e sada qual est associado o arquivo. Estas duas declaraes podem ainda estar munidas da especificao IOSTAT=, para detectar possveis erros no posicionamento do arquivo, sem causar a descontinuao.
43
Antes de comear a usar um array necessrio declar-lo, quanto ao tipo e quantidade de elementos possui, para informar ao compilador que tipo de dado ser armazenado e quanto ser usado de memria. Abaixo apresenta-se algum declaraes de arrays:
real, dimension(16) :: x
aqui tem-se um array de rank 3 x com 102030 elementos do tipo integer; O nmero de elementos do array em uma dada direo chama-se extenso do array naquela direo. A forma de um array definida pelo seu rank e pelas extenses do array nas vrias direes. Dois arrays tem a mesma forma se possuem o mesmo rank e as mesmas extenses em cada direo. O tamanho de um array o total de elementos declarados naquele array. Pode-se definir arrays constantes. Um array constante aquele que possui todos seus elementos constantes. Inicializa-se um array constante usando um construtor de array. Abaixo apresenta-se um exemplo de uso do construtor de array
real, dimension(5) :: w w = (/1.,2.,3.,4.,5./)
as duas /s existentes no exemplo acima funcionam como o incio e o final dos dados do array sendo inicializado.
5.2 Usando elementos de array em declaraes Fortran
Aqui apresenta-se algumas informaes de como utilizar elementos de arrays em declaraes Fortran.
5.2.1 Usando elementos de array em declaraes Fortran
Cada elemento de um array uma variavel tal como todas as outras j at o momento nos captulos anteriores, e portanto podem ser inseridas em qualquer espresso aritmtica ou lgica.
5.2.2 Inicializando elementos de array
Os elementos de um array podem ser inicializados por declarao de atribuio. Abaixo apresenta-se alguns exemplos
integer, dimension(3) :: k k(1) = 5 k(2) = 4 k(3) = 1
neste caso o array k, com elementos inteiros, foi inicializado utilizando um declarao para cada elemento. Para arrays pequenos isto possvel, para arrays grandes melhor utilizar estruturas de laos para efetuar tal tarefa. Assim vejamos o seguinte exemplo
integer, dimension(1000) :: k
45
Boa Prtica
O bloco de programa acima tambm podem ter sido inicializado utilizando-se um lao implcito (uma forma especial de lao do tipo DO). Como segue
integer, dimension(1000) :: k = (/(i, i = 1,1000)/)
Os elementos de um array rank 1 so endereados normalmente numerando o primeiro de 1, o segundo de 2, e o n-simo de n. Isto pode ser diferente. Imagine um array com 11 elementos, seus elementos seriam normalmente numerados de 1 a 11. No entanto tambm poderamos numer-los de 5 a 5, que a quantidade de elementos seria os mesmo 11. O Fortran suporta este tipo de nomenclatura que expressa da seguinte forma para um array rank 1
real, dimension (valor_baixo:valor_alto) :: x
para ranks maiores procede-se de maneira semelhante ao rank 2. A dimenso numa dada direo (i) obtida por
extensao_i = valor_alto_i valor_baixo_i + 1 5.2.4 Subscritos de array fora da faixa de definio
Para um array rank 1 com, digamos 10 elementos, ao se tentar acessar o suposto dcimo quinto elemento, o que acontecer? Isto depende de processador para processador. Em alguns o programa rodando verificar se todos os elementos usados esto na faixa adequada. Caso seja identificado casos fora da faixa, o programa emitir um mensagem e descontinuar a execuo. Entretanto este processo torna os programas mais lentos. Assim em geral esta capacidade de fazer a verificao de limites de array opcional nos compiladores. Desta forma aconselhvel que esta capacidade fique ligada quando o programa estiver sendo criado e compilador.
46
Boa Prtica
O uso de constantes pode ser utilizado para melhorar e tornar mais claro a declarao de arrays. Alm disto a definio constante protege o valor contra a corrupo de seu valor. Abaixo mostra-se alguns exemplos de tal uso
integer, parameter :: tamanho = 1000 real :: array1(tamanho) real :: array2(tamanho+5) real :: array3(tamanho*tamanho) real :: array4(tamanho,tamanho)
Boa Prtica
Sempre use constantes para declarar o tamanho de arrays em programas Fortran, visando torn-los fceis de ler, e de modificar.
Conforme falado anteriormente possvel fazer as operaes clssicas da lgebra sobre os elementos de um array, numa estratgia elemento-a-elemento. Vamos ver nesta seo como fazer operaes sobre arrays completos ou sobre subconjuntos de arrays.
5.3.1 Operaes com arrays completos
Sob certas circunstncias possvel efetuar operaes sobre arrays completos com uma notao similar quela usada para variveis escalares. Se dois arrays tem a mesma forma, eles podem ser usados em algumas operaes aritmticas como se fossem escalares. Algumas funes intrnsecas aplicam-se a arrays completos. Abaixo mostra-se dois programas, um escrito na forma tradicional, ou escrito usando operaes sobre arrays completos. Forma tradicional
! Este programa mostra operaes sobre arrays, escritas da forma tradicional
47
48
Fica claro que todos os resultados impressos so iguais a 2.0. Na operao com arrays completos basta que os arrays envolvidos numa operao tenha a mesma forma, no importando se as faixas das extenses de cada array em cada direo usem notao diferenciada. Se dois arrays no tem a mesma forma ento podero executar operaes do tipo array completo.
5.3.2 Definindo e operando com subconjuntos de arrays
Para definir um subconjunto de um array ou seo de array (array section) fazse necessrio usar uma trinca de ndices que tem a forma geral
indice_1 : indice_2 : incr
onde indice_1 o primeiro subscrito a ser includo no subconjunto do array; indice_2, o ltimo subscrito a ser includo; e incr o incremento que se far ao longo do array completo. Esta definio pode ser aplicada para cada direo do array. Abaixo apresenta-se alguns exemplos de definio de subconjuntos de arrays. Sejam os seguintes arrays originais
integer, dimension(7) :: array1 = (/1,2,3,4,5,6,7/) integer, dimension(4,4) :: array2 = (/1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16/)
49
Um ou todos os elementos da trinca podem ser default. Se indice_1 no aparece na trinca o seu default o primeiro ndice de elemento para aquela direo. Se indice_2 no aparece na trinca o seu default o ltimo ndice de elemento para aquela direo, e incr no aparece na trinca o seu default a unidade. Todas as combinaes abaixo so possveis e legais
indice_1 : indice_2 : incr indice_1 : indice_2 indice_1 : indice_1 :: incr : indice_2 : indice_2 : incr :: incr : 5.3.3 Reformatando arrays
Um array rank 1 com cem elementos pode ser reformatado para, por exemplo, para um array rank 2 (1010) ou mesmo um array rank 2 (520), mantendo os valores e o mesmo nome. Para tal utiliza-se a funo intrnseca RESHAPE cuja formato
array3 = reshape(array1, array2)
onde array1 contm os dados para reformatar e array2 um array rank 1 descrevendo a nova forma. O nmero de elementos em array2 igual ao nmero de dimenses no array de sada e os seus valores igual dimenso em cada direo. O nmero de elementos em array1 deve o mesmo nmero de elemento para forma especificada para array2, caso contrrio a declarao reshape ir falhar. O array3 o resultado da reformataro, quando bem sucedida. Abaixo apresenta-se um programa que reformata um vetor com cem elementos para uma matriz quadrada (1010).
! Este programa mostra a reformatao de arrays. ! program reformatacao_de_arrays implicit none
50
Como o construtor de arrays s funciona com arrays rank 1, a declarao reshape serve para transformar arrays criados rank 1 em arrays que no sejam rank 1.
5.4 Usando funes intrnsecas Fortran com arrays
O Fortran 90/95 tm trs tipos de funes intrnsecas: funes elementares (elemental functions), funes de questionamento (inquiry functions), e funes de transformao (transformational functions). Algumas funes de cada um destes grupos so projetadas para operar com arrays.
5.4.1 Funes intrnsecas elementares
So funes que projetadas para operar com escalares trabalham tambm com arrays. Se o argumento de uma funo elementar um escalar o resultado ser um escalar, se o argumento um array o resultado ser tambm um array. Estes dois arrays devero ter o mesmo formato. A maioria das funes intrnsecas que aceitam escalares como argumentos tambm aceitam arrays. Alguns exemplos so
y = sin(x) y = cos(x) y = sqrt(x)
Funes intrnsecas de questionamento so funes cujo resultado depende do formato e da natureza do argumento. Por exemplo LBOUND(force) uma funo de questionamento que retorna o menor subscrito do array force. Na Tabela 5.1 apresenta-se algumas destas funes mais usadas.
51
Funes intrnsecas de transformao so funes que tem um ou mais arrays como argumento ou um array como resultado. Este tipo de funes no operam sobre escalares, apenas sobre arrays completos. Freqentemente o resultado deste tipo de funo um array com formato diferente dos arrays de entrada. O Fortran 90/95 tm muitas funes intrnsecas de transformao, algumas so apresentadas na Tabela 5.2. Tabela 5.2-Algumas funes de transformao comuns. Funo e agumentos Utilidde all(mask) Funo lgica, retorna TRUE se todos os valores no array mask so TRUE any(mask) Funo lgica, retorna TRUE se qualquer dos valores no array mask TRUE count(mask) Retorna a quantidade de elementos TRUE em mask dot_product(vet_1,vet_2) Calcula o produto interno de dois arrays rank 1 de mesmo formato matmul(matriz_1,matriz_2) Multiplica duas matrizes conformveis maxloc(array,mask) Retorna o local onde ocorre o elemento de maior valore entre aqueles para os quais MASK TRUE. O resultado um array rank 1 contendo os subscritos da posio. minloc(array,mask) Retorna o local onde ocorre o elemento de menor valore entre aqueles para os quais MASK TRUE. O resultado um array rank 1 contendo os subscritos da posio. minval(array,mask) Retorna o menor valor no array entre aqueles para os quais MASK TRUE. (mask opcional) product(array,mask) Calcula o produto dos elementos de um array para os quais MASK TRUE. MASK opcional, se no estiver presente o produto feito com todos os elementos do array.
52
transpose(matriz) maxval(array,mask)
Considere a necessidade de se fazer algum tipo de operao apenas sobre alguns elementos de um array e outro tipo sobre os restantes. O Fortran 90/95 suporta operao sobre array considerando uma mscara que informa sobre quais elementos deve-se fazer ama operao e quais devem ficar com a outra operao. Esta mscara em realidade um array lgico com o mesmo formato do array a ser operado. Deve-se colocar nesta mscara, nas posies correspondentes s quais deseja-se fazer a primeira operao no array, valores igual a TRUE, e no restante das posies coloca-se FALSE.
5.5.1 A estrutura WHERE do Fortran 90
No cdigo acima faz-se declaracoes_com_array_1 sobre todos os elementos do array para os quais os elementos correspondentes de mask TRUE. Para aquelas posies correspondentes aos elementos com valor FALSE em mask aplica-se declaracoes_com_array_2.
Boa Prtica
Use a estrutura WHERE para modificar somente os elementos de um array que passem em um determinado teste .
O Fortran 95 apresenta uma forma mais complexa de estrutura WHERE, onde permite-se o uso de vrios ELSEWHERE com mscara e apenas um ELSEWHERE sem mscara. A forma geral da estrutura WHERE do Fortran 95
{nome:} where (mask_1) declaracoes_com_array_1 Verso preliminar. Escrito em 12/01/99. Impresso em 08/25/05
53
O Fortran 90/95 tambm tem uma estrutura WHERE com apenas um linha de declarao. Sua forma
where(mask) declaracoes_com_array
neste caso ser feito determinado conjunto de operaes apenas sobre os elementos do array que correspondem aos elementos com valores TRUE em mask.
5.6. A estrutura FORALL
O Fortran 95 apresenta um novo tipo de estrutura, o FORALL, que foi projetado para permitir um conjunto de operaes a serem aplicadas a um conjunto de elementos de um array. Os elementos a serem operados so definidos pelos ndices e tambm por uma expresso lgica. As operaes ocorrero apenas sobre os elementos do array que atendam as restries contidas nos ndices bem como aquela contida numa expresso lgica.
5.6.1 A forma da estrutura FORALL
Todos os arrays utilizados at o momento tiveram suas dimenses definidas durante o perodo de compilao e no podem ser alteradas durante a execuo do programa. Esta a chamada alocao esttica de memria, uma vez que o tamanho dos arrays nunca muda durante a execuo do programa. desejvel que o tamanho de um array varie durante a execuo para se moldar s necessidades de armazenamento, que variam de caso a caso sendo executado. Uma melhor soluo
54
note que na declarao acima o tipo do array fica definido, mas no o seu tamanho. Para o programa definir durante a execuo o tamanho do array necessrio o uso da declarao executvel ALLOCATE que tem a seguinte forma
allocate (lista_de_arrays_para_alocar, STAT = status)
Um exemplo tpico
allocate(array1(50,-10:50), STAT = status)
Boa Prtica
Caso se tenha dvida se um array alocvel j est alocado, pode-se descobrir esta informao usando a funo de questionamento ALLOCATED(array). Se array j estiver alocado o retorno ser TRUE, caso contrrio ser FALSE. Aps utilizar um array alocvel necessrio desaloc-lo para liberar memria til para outros propsitos. Isto pode ser feito usando a declarao executvel DEALLOCATE. A forma geral desta declarao
deallocate (lista_de_arrays_para_desalocar, STAT = status)
Um exemplo caracterstico
deallocate(array1, STAT = status)
onde a especificao STAT = tem o mesmo significado daquela usado na declarao ALLOCATE.
55
Boa Prtica
Sempre utilize desalocamento dinmico de arrays usando uma declarao DEALLOCATE, assim que os mesmos no for mais utilizado.
56
Boa Prtica
Divida grandes programas em sub-programas para beneficiar-se das vantagens de teste independente, reuso do cdigo, e encapsulamento (parcial) de dados.
57
Uma subrotina um sub-programa Fortran que invocado utilizando a declarao CALL antes do nome da subrotina, ela recebe alguns argumentos de entrada e retorna outros argumentos de sada. A forma geral de declarar uma subrotina
subroutine {nome} (lista_de_argumentos) declaracoes_no_executaveis declaracoes_executaveis return end subroutine {nome}
No bloco de cdigo acima a primeira linha marca o incio da subrotina e a ltima linha marca o final da subrotina. A declarao return retorna o controle da execuo ao programa principal, da mesma forma que um programa pode ter vrios STOPs, uma subrotina pode ter vrios RETURNs. O nome da subrotina pode ter at 31 caracters porm o primeiro deve ser alfabtico. Na lista _de_ argumentos tm-se as variveis e arrays que sero passadas pelo programa principal quando invocar a subrotina. Como uma subrotina (ou funo) um programa independente pode-se utilizar variveis com os mesmos nomes de variveis usadas no programa principal e outras subrotinas sem a possibilidade de conflito. Uma subrotina no pode chamar a si mesma, a no que tenha sido declarada recursiva (recursive). A forma de invocar uma subrotina como segue
call {nome} (lista_de_argumentos)
onde os argumentos na lista_de_argumentos deve ter uma relao biunvoca com os listados na declarao de argumentos da subrotinas, quanto quantidade, ao tipo e ao uso que se destina.
Cuidado
Confira que os argumentos listados no programa principal e em um subrotina estejam adequadamente representados em nmero, ordem e tipo.
58
Este programa quando executado a passagem de dados feita de uma forma apropriada e o resultado produzido o correto, z = 4.0. Quando um programa passa um argumento para uma subrotina, em realidade ele est passando um ponteiro que aponta para a posio de memria onde aquela varivel est, com isto a subrotina pode fazer uso do seu valor. Fica claro que a subrotina pode modificar o valor que est naquele endereo mediante uma simples operao de atribuio. Em realidade isto que ocorre com os chamados argumentos de retorno, a subrotina escreve algo naquela posio de memria que foi indicada pelo programa principal, a questo se de entrada ou de sada (at este ponto) est mais na cabea do programador. Percebe-se ento que possvel haver a corrupo dos dados do programa principal atravs de uma alterao indevida ocorrida em uma subrotina. A seguir apresenta-se um programa onde pode haver algum tipo de corrupo de dados
program corrupcao_de_dados !O objetivo deste programa eh mostrar a corrupcao de !dados quando se utiliza subrotinas ! !Programmer: joao batista aparecido ilha solteira, 11 dezembro 1999 ! implicit none real :: x = 1.5, y = 2.5, z !invocando a subrotina call soma(x,y,z) stop end program corrupcao_de_dados subroutine soma(a,b,c) implicit none real :: a,b,c !corrompendo o dado b. Isto pode ocorrer por digitacao distraida em um cdigo !grande, por fragmentos de codigo nao deletado e assim por diante. b=b*2 !soma dois reais c=a+b
59
Neste caso o resultado deveria ser z = 4.0 no entanto o resultado ser z = 6.5, isto ocorreu porque uma operao dentro da subrotina que alterou o valor que estava armazenado no endereo da varivel y, neste ficam os valores e y e z corrompidos. Dependendo da estrutura do programa a informao corrompida poder se propagar contaminado os resultados intermedirios e o resultado final. Agora apresenta-se outro caso de corrupo de dados, via definio incorreta dos tipos das variveis na subrotina
program corrupcao_de_dados_2 !O objetivo deste programa eh mostrar a corrupcao de dados !quando se utiliza subrotinas ! !Programmer: joao batista aparecido ilha solteira, 11 dezembro 1999 ! implicit none real :: x = 1.5, y = 2.5, z !invocando a subrotina call soma(x,y,z) stop end program corrupcao_de_dados_2 subroutine soma(a,b,c) implicit none !introduzindo a corrupo nos dados. Via uma declarao & !incorreta de tipo na subrotina integer :: a,b,c !soma dois reais c=a+b return end subroutine soma
A execuo deste programa no ir corromper o valor da varivel y mas ir produzir um resultado totalmente errado para a varivel z. Apresenta-se a seguir a soluo para os problemas de corrupo de dados mostrados no primeiro exemplo, posteriormente retornaremos aos apresentados no segundo exemplo.
6.1.1 O atributo INTENT
Percebe-se da anlise dos dois exemplos apresentados acima que duas formas de corrupo de dados so: via a modificao acidental do valor das variveis de entrada
60
61
Quando da compilao, o compilador acusar de imediato o erro, e mostrar claramente qual o erro e onde encontra-se. Basta compilar para ver!
Boa Prtica
Como falado anteriormente, um escalar passando do programa principal para um subrotina simplesmente passando um ponteiro que aponta para a varivel. Se o argumento um array o programa principal passa um ponteiro que aponta para o primeiro elemento do array. A pergunta que surge : Como a subrotina fica sabendo do da forma e das extenses do array? O rank do array ser definido no atributo dimension colocado na subrotina, mas ainda fica faltando a informao sobre a extenso do array em cada direo. Existem trs formas de informar subrotina quais as extenses do array em cada direo. A primeira forma de se fazer isto passar as extenses do array via argumentos da prpria subrotina e ento utilizar esta informao em uma declarao de atributo dimension e definir por completo a forma do array. Os arrays passados desta forma so denominados de arrays com forma explicita, uma vez que sua forma passada explicitamente. A seguir apresenta-se um programa que utiliza este tipo de passagem de argumentos do tipo array
program passando_array_1 !Objetivo: Mostrar a passagem de um array do program principal !para um subrotina de forma explicita ! !programmer: joao batista aparecido ilha solteira, 11 dezembro 1999 ! implicit none integer, parameter :: i_max = 100, j_max = 120 real, dimension(i_max,j_max) :: array1, array2 !atribuio de valores aos arrays array1=5.0 !invocando a subrotina passa_array call passa_array(i_max, j_max, array1, array2) stop end subroutine passa_array(i,j,x,y) implicit none integer, intent(in) :: i, j Verso preliminar. Escrito em 12/01/99. Impresso em 08/25/05
62
Note que este tipo de passagem de array suporta operaes sobre arrays completos. Uma segunda maneira de se passar as informaes de um array para o compilador declarar na subrotina cada extenso do array com um asterisco. Neste caso o compilador no sabe nada sobre as dimenses do array, e as operaes de verificao de limites do array e operaes arrays inteiros no so permitidas. Os arrays passados desta forma so chamados de arrays de tamanho presumido. O programa mostrado acima adaptado para fazer passagem de array neste novo formato, a seguir o cdigo
program passando_array_2 !Objetivo: Mostrar a passagem de um array do program principal !para um subrotina de forma implicita ! !programmer: joao batista aparecido ilha solteira, 11 dezembro 1999 ! implicit none integer, parameter :: i_max = 100, j_max = 120 real, dimension(i_max,j_max) :: array1, array2 !atribuio de valores aos arrays array1=5.0 !invocando a subrotina passa_array call passa_array(i_max, j_max, array1, array2) stop end subroutine passa_array(i,j,x,y) implicit none integer, intent(in) :: i, j integer :: m !note a seguir os asteriscos nos atributos dimension real, intent(in), dimension(*) :: x real, intent(out), dimension(*) :: y do m=1,i*j y(m) = sqrt(x(m)) end do
63
Note que neste caso o programa no suporta a operao sobre array completo y = sqrt(x), intuitivo que no lugar desta expresso devssemos colocar dois laos de DO uma vez que no programa principal estes arrays so rank 2. No entanto isto no funcionou, porque como o compilador no sabe nada sobre o rank nem as extenses do array em cada direo, ele interpreta que trata-se um um array rank 1, no formato em que os dados so armazenados na memria. Ento ao invs de colocar dois DOs, para cobrir as duas extenses dos arrays rank 2, tive que colocar apenas um DO com lao igual ao tamanho (quantidade de elementos) do array. A ento funciona.
Cuidado
Nunca use passagem de arrays para subrotinas pelo mtodo do tamanho presumido..
Uma terceira forma de fazer a passagem de array utilizando arrays de forma presumida juntamente com uma interface explicita para a subrotina. Veremos mais adiante neste captulo como fazer isto.
Boa Prtica
Sempre use passagem de arrays pelo mtodo da forma explicita ou da forma assumida com interface explicita..
Quando uma varivel caracter passada para um subrotina no necessrio passar o tamanho da varivel, bastando colocar um asterisco no local onde apareceria o seu tamanho, no cdigo da subrotina. Se for necessrio saber o tamanho da varivel caracter pode-se usar a funo intrnseca LEN(variavel_caracter). Ver exemplo abaixo
program passando_caracter implicit none character(len = 5) :: word = 'manga' call caracter(word) stop end program passando_caracter subroutine caracter(x) implicit none character(len = *)x write(*,*)'O tamanho da varivel caracter ',x,' eh = ',len(x)
64
Os valores de todas as variveis locais de um subrotinas tornam-se indefinidos toda vez que a subrotina termina de executar. Surge ento a seguinte pergunta: Quando a subrotina for acessada por um segunda vez qual ser o valor inicial assumido por uma determinada varivel local da subrotina? Pela afirmao na primeira linha deste pargrafo pode-se dizer que o valor ser indeterminado, uma vez que o programa poder estar destinando um novo endereo na memria para aquela varivel local. Pode acontecer tambm a posio de memria ocupada pela varivel local pela primeira vez tenha sido usada posteriormente para outra finalidade e o seu valor tenha mudado. De qualquer forma evite fiar-se em valores volteis armazenados na memria entre um chamada e outra da subrotina, embora algumas vezes estes valores parecem permanentes. O Fortran prov um mecanismos para tornar perene o valor de uma varivel local de uma subrotina, normalmente voltil entre duas chamadas. o atributo SAVE que perenizar o valor da varivel local entre chamadas consecutivas de uma subrotina. Sua declarao como segue
real, save :: lista_de variveis integer, save, dimension (5,6) :: x,y 6.3 Arrays automticos
Aprendeu-se no captulo anterior a alocar arrays alocveis em programas principais bem como a desaloc-los. Pode-se fazer o mesmo em subrotinas, no entanto existe uma forma mais fcil e prtica de conseguir o mesmo efeito. Considerando que toda varivel local de subrotina voltil, ou seja ela torna-se indefinida depois que a execuo da subrotina termina, pode-se criar arrays locais, passando apenas as extenses do array via argumentos da subrotina. Veja a seguinte subrotina criando um array rank 2 local chamado array1 com extenses p e q
subroutine array_automatico(p,q) implicit none real, dimension array1(p,q,) outras_declaracoes_executaveis return end subroutine array_automatico Use arrays automticos para criar locais temporrios de trabalho em subrotinas. Use arrays alocveis para criar locais de trabalho temporrios de trabalho no programa principal ou que vai ser criado em uma subrotina e destrudo em outra. Em
Boa Prtica
65
Pelo visto at o momento o programa principal e as subrotinas so programas distintos que trocam informaes entre si, via uma lista de argumentos. O Fortran 90/95 possui uma nova forma de passar dados (e outras informaes tambm, como veremos mais adiante) que mais flexvel e segura. Assim programas Fortran, subrotinas e funes podem compartilhar informaes usando mdulos (modules). Mdulo uma unidade de programa compilada separadamente que as definies e valores iniciais dos dados que deseja-se compartilhar entre o programa e os sub-programas. Para uma unidade de programa acessar os dados no mdulo, basta mencionar o nome do mdulo logo aps a declarao USE. Esta declarao deve ir logo aps a declarao do nome do programa ou sub-programa. Para que os dados contidos no mdulo no fiquem volteis necessrio utilizar a declarao SAVE na seo de declarao de variveis. A forma geral de definir um mdulo
module nome_do_modulo declaracoes_nao_executaveis end module nome_do_modulo
na primeira linha aps o cabealho do programa ou sub-programa. A seguir temos um exemplo de um mdulo para compartilhar constantes.
module constantes implicit none save !definindo as constantes real :: pi = 3.14159, e = 2.718281828 end module constantes program modulo !Objetivo: Mostrar o uso de modulos para compartilhar dados entre programas. ! !programmer: joao batista aparecido ilha solteira, 11 de dezembro 1999 ! use constantes implicit none integer, parameter :: i_max = 10 real, dimension(i_max) :: x,y,z integer :: i do i=1,i_max x(i) = sqrt(real(i)/i_max) y(i) = cos(real(i)*pi*x(i))
66
Alm de dados os mdulos podem conter subrotinas e funes, que so chamadas ento subprogramas-mdulo. Estes subprogramas-mdulo so compilados como componentes do mdulo e so disponibilizados para as unidades de programa atravs do uso da declarao USE. Os subprogramas inclusos em um mdulo devem vir depois da declarao de dados compartilhados e devem ser precedidos pela declarao CONTAINS. Esta declarao diz ao compilador que o restante de cdigo so subprogramas inclusos. Assim um mdulo pode conter uma coleo de subrotinas, como segue
module banco implicit none save real :: pi = 3.14159, e = 2.718281828 contains subroutine sub1(x,y) implicit none real, intent(in) :: x real, intent(out) :: y y=sin(pi*x) return end subroutine sub1 subroutine sub2(m,x,y) implicit none real, intent(in) :: x real, intent(out) :: y integer, intent(in) :: m y = exp(real(m)*x)/e
67
Sabe-se que possvel criar e compilar subrotinas separadamente e depois coloc-las juntas usando o linker. Ento porque ter o trabalho extra de colocar estas subrotinas em um mdulo, compilar e depois lincar de novo? A razo que o mdulo prov uma maneira superior de compartilhamento de dados, de tal forma que mais informaes sobre as subrotinas estaro disponveis ao compilador, que poder ento fazer um melhor controle do programa. Quando o programa (usando mdulo) compilado, o compilador verifica o nmero de argumentos, os tipos dos argumentos, se o argumento um array ou no, os atributos INTENT de cada argumento, e assim por diante. Um sub-programa compilado dentro de um mdulo e acessado por uma declarao USE tm uma interface explicita. Ento o compilador do Fortran automaticamente sabe explicitamente todos os detalhes sobre cada argumento no subprograma sempre que este usado e verifica a interface para confirmar que est sendo usada adequadamente. Quando os subprogramas no esto dentro de um mdulo o compilador tm pouca informao sobre os subprogramas. Estes subprogramas tm um interface implcita. Um exemplo disto o programa mostrado acima chamado corrupcao_de_dados_2 onde os argumentos da subrotina esto declarados de forma errada. Enquanto deveriam ser reais, esto declarados como inteiros. Mais adiante mostraremos a soluo para evitar este tipo de corrupo de dados utilizando o conceito de mdulo. Quando compilando um programa deste tipo, o compilador apenas assume que tudo transcorra bem, os seja que a quantidade e os tipos dos argumentos esto corretos, que esto na ordem correta, e que o tamanho dos arrays esto corretos. Se algo estiver errado o programa falhar totalmente. Mostra-se abaixo o programa corrupcao_de_dados_3 que o corrupcao_de_dados_2 com a subrotina escrita dentro de um mdulo.
module auxiliar contains subroutine soma(a,b,c) implicit none !introduzindo a corrupo nos dados. Via uma declarao&
68
Aps a compilao deste programa (usando o Compildor da Microsoft V4.0) percebe-se que o resultado no to bom quanto esperado. O compilador, dependendo das diretivas internas (trs: no warning, normal warning, and warning treated as error) pode acontecer trs coisas: 1) na opo no warning, no emite nada, ignorando o erro; 2) na opo normal warning, identifica claramente o erro mas emite mensagem apenas de warning; e 3) na opo warning treated as error o compilador emitiu os mesmos warnings do item 2, mas identificou que tinha um no global um erro. digno de nota que o resultado apresentado quando compilando os programas corrupcao_de_dados_3 e corrupcao_de_dados_2 foram praticamente identicos, ou seja este compilador est implicitamente instrudos a fazer a verificao dos argumentos de um subrotina mesmo quando no so uma interface explicita, a literatura mostra que nem todos compiladores fazem isto.
Boa Prtica
Quando ainda estiver desenvolvendo um programa, sempre use as opes de emisso de warnings da forma mais restritiva possvel, para evitar deixar escapar erros importantes disfarados de simples warnings.
6.5.2 Passando arrays para subrotinas usando mdulos e arrays de forma assumida.
Como dito anteriormente, quando uma subrotina tem uma interface explicita, toda a informao necessria sobre os argumentos estaro disponveis para o
69
70
interessante notar que neste mtodo de passagem de arrays no necessrio passar explicitamente a extenso do array em cada direo, estes valores podem ser obtidos na prpria subrotina usando as declaraes LBOUND e UBOUND. Os arrays passando assim suportam operaes sobre array completo.
Para passagem de arrays entre unidades de um programa d preferencia a passagem explicita ou a passagem de array na forma assumida. Estas duas tcnicas so mais seguras e ambas suportam operaes sobre arrays completos. Evite usar a tcnica do array com tamanho assumido que menos segura e no suporta operaes sobre arrays completos.
Boa Prtica
6.6 Funes Fortran 90/95
Uma funo um subprograma Fortran que produz apenas um resultado de sada, embora podendo ser um array. O nome da funo pode ser usado no lado direito de uma funo aritmtica como uma varivel qualquer. O Fortran tm suas prprias funes intrnsecas (j falado anteriormente) e suporta tambm funes definidas pelo programador. O uso das funes definidas pelo usurio similar aquele das funes intrnsecas. A forma geral de uma funo
function nome_da_funcao (lista_de_argumentos) declaracaoes_no_executaveis declaracoes_executaveis nome_da_funcao = expresao return end function nome_da_funcao
A funo deve comear com a declarao FUNCTION e terminar com a declarao END FUNCTION. O nome da funo pode conter at 31 caracteres sendo que o primeiro deve ser alfabtico. Uma funo invocada quando seu nome mencionado em uma expresso aritmtica. Quando isto ocorre o programa para a execuo e passa o controle da execuo para a primeira linha da funo que efetua sua execuo, quando esta termina, o controle retorna ao programa que invocou a funo. Quando a funo retorna o seu valor usado para continuar o processamento onde ocorria seu nome na expresso aritmtica. Uma funo pode ser vazia de argumentos, neste caso deve-se colocar os dois parnteses sem nada entre. Como o nome da funo retorna um valor, ento este nome deve ser declarado na seo de declarao de variveis, tanto no programa que invoca quanto na prpria funo. A passagem de argumentos nas funes ocorre da mesma forma que nas subrotinas.
program funcao !Objetivo: Mostrar a tcnica de uso de uma function, calculando !o valor assumido por uma parabola, sendo conhecido os coeficientes
71
Duas novas classes de funes disponveis em Fortran 90/95, destinadas principalmente (porm no somente) ao processamento paralelo do tipo SIMD (single instruction multiple data) so as funes puras (pure functions) e as funes elementares (elemental function).
6.7.1 Funes puras (pure functions) Funes puras so aquelas que no tm efeitos colaterais, isto os seus argumentos no podem ser modificados, nem outros dados podem ser modificados tais como aqueles contidos em mdulos. Adicionalmente, variveis locais no podem ter o atributo SAVE, e tambm no podem ser inicializadas em declaraes de tipo (uma vez que isto implicitamente implicaria em SAVE). Qualquer funo invocada por uma funo pura tambm dever ser pura. Todos os argumentos de uma funo pura devem ter a declarao de atributo INTENT(IN), alm dos mais no devem fazer nenhuma operao de entrada e sada de dados. Uma vez que funes puras no tem efeitos colaterais, elas so ideais para se utilizar junto com a declarao FORALL, onde podem ser executadas em (paralelo) qualquer ordem. Funes puras propriamente ditas s so definidas no Fortran 95, embora seja possvel criar algo parece em Fortran 90 declarando todos os argumentos da funo com o atributo INTENT(IN). Para declarar um funo pura basta acrescentar o prefixo PURE antes da palavra FUNCTION como segue
pure function nome_da_funcao (lista_de_argumentos)
72
Para declarar um funo elementar basta acrescentar o prefixo ELEMENTAL antes da palavra FUNCTION como segue
elemental function nome_da_funcao (lista_de_argumentos)
Quando um subprograma invocado, a lista de argumentos passada como um conjunto de ponteiros que apontam para as posies de memria ocupadas por aqueles argumentos. Como cada posio de memria interpretada pelo subprograma depende do tipo e tamanho declarado no subprograma. Esta passagem por referncia pode ser estendida para permitir passar tambm ponteiros que apontem para outros subprogramas. Em Fortran funes e subrotinas podem ser passadas como argumento para outras funes ou subrotinas.
6.8.1 Passando funes como argumento
Se o nome de uma funo mencionado na lista de argumentos de um subprograma, ento um ponteiro passado para o subprograma. Se o argumento correspondente funo for mencionado internamente no subprograma, quando o subprograma executado, ento a funo receber o comando da execuo. Todo funo usada como argumento dever ter o atributo EXTERNAL. Abaixo apresenta-se um exemplo de uma subrotina que extrai valores de uma funo em certas posies definidas pela subrotina.
program funcao_como_argumento !Objectivo: Mostrar o uso de uma funo Fortran como argumento de !um outro subprograma: funo ou subrotina. ! !programmer: joao batista aparecido ilha solteira, 12 dezembro 1999
73
74
Como j visto anteriormente o tipo real de dados usado para representar nmeros com ponto flutuante. Na maioria dos computadores hoje operando o tamanho default da varivel real, tambm chamado de preciso simples (single precision) usualmente 4 bytes, ou seja 32 bits. Conforme explicado em captulo anterior estes 32 bits so divididos em dois grupos, um para representar a mantissa e o outro para representar o expoente. A implementao padro de tipos de dados reais com preciso simples em geral tero uma preciso de 7 casas decimais e os nmeros estaro em uma faixa de 10-38 a 10+38. Caso seja necessrio ou desejvel representar nmeros reais com mais de 7 casas decimais e com uma faixa maior que de 10-38 a 10+38, ento ser necessrio utilizar variveis reais com tamanhos maiores que 32 bits. O Fortran possui tipo de dados com tamanho maior para representao de dados com maior preciso e maior faixa. Tipo de dados reais com maior tamanho tambm so de dupla preciso (double precision). Uma varivel real com dupla preciso tem usualmente 8 bytes ou 64 bits, nestes casos a implementao mais comum usar 53 bits para a mantissa e 11 bits para o expoente. Os 53 bits da mantissa so suficientes para representar aproximadamente 16 casas decimais, e os 11 bits do expoente so suficientes para representar nmeros em uma faixa de 10-308 a 10+308.
7.1.1 Tamanhos (kinds) de dados para variveis e constantes do tipo real
Como o Fortran prov para tipos de dados reais mais de um tamanho (kind) necessria a existncia de uma maneira de declar-los. Para tal existe o parmetro de tamanho (kind type parameter) para declarar os diferentes tamanhos. A seguir tem-se algumas exemplos de como efetuar esta declarao de tamanho de variveis ou constantes reais
real(kind = 4) :: r,s ou real(4) :: r,s real(kind = 8) :: w ou real(8)
Cada fabricante de compilador livre para definir o tamanho da palavra para cada representao de dados reais. Assim kind = 4 pode na maioria dos computadores pode significar uma palavra com 32 bits, mas em outros pode significar uma palavra com 64 bits. Assim o fragmento de cdigo escrito acima torna-se dependente de cada processador. Na Tabela 7.1 apresenta-se o tamanhos e os tipos de nmeros reais de alguns compiladores Fortran 90. Uma maneira mais elegante, mais ainda dependente do processador, de escrever o trecho de cdigo acima
integer, parameter :: single = 4, double = 8 real(kind = single) :: r,s ou real(single) :: r,s real(kind = double) :: w ou real(double)
75
Nas atribuies acima, a primeira atribuio interpreta 37.5 como sendo um dado do tipo preciso simples (32 bits); na segunda 37.5 representado por um tamanho que corresponde ao parmetro de tamanho igual a 4, nestes casos em geral mas nem sempre (ver tabela acima) corresponde tambm a 32 bits; a terceira corresponde exatamente primeira; na quarta 37.5 representado por em preciso dupla em geral correspondendo a 64 bits; a quinta corresponde exatamente quarta.
Sempre defina um parmetro de tamanho em uma constante, e ento use esta definio como atributo em declaraes de variveis e constantes. Esta prtica ajudar portar mais facilmente o programa em outras plataformas. Pode-se tambm colocar as constantes que definem os parmetro de tamanho em um nico mdulo e ento referenciar o mdulo em todas as unidades do programa.
Boa Prtica
O Fortran 90/95 prov uma funo intrnseca KIND para determinar o parmetro de tamanho de um varivel. O programa abaixo mostra como usar a funo KIND
program usando_kind implicit none integer, parameter :: double = 8 write(*,*)kind(37.5), kind(37.5_4), kind(37.5e0), kind(37.5d0), & kind(37.5_double) stop end program usando_kind
76
As expresses preciso simples e preciso dupla no so nicas em Fortran, uma vez que em um processador a preciso simples pode ser usando 64 bits e a preciso 128 bits. No entanto outros computadores utilizam 64 bits para dupla preciso e 32 bits para preciso simples. Desta forma dependendo do processador que se usa a interpretao do que simples e dupla preciso poder variar, implicando muitas vezes em resultados totalmente inesperados. interessante utilizarmos alguma maneira de definir a preciso de um varivel de uma forma o mais independente possvel do processador. O Fortran 90/95 apresenta uma funo intrnseca que auxilia nesta tarefa. A funo chamada SELECTED_REAL_KIND, quando executada retorna o parmetro de tamanho necessrio a uma varivel real, que corresponde a uma preciso e faixa especificadas, para aquele processador especfico. A forma geral desta funo
parametro_de_tamanho = selected_real_kind(p,f)
onde p a preciso em casas decimais desejada e f a faixa do expoente requerido em potncia de dez. A funo retorna o menor parmetro de tamanho que satisfaz as condies especificadas. Se esta funo retorna 1 significa que nenhum parmetro de tamanho satisfaz a preciso especificada; se retorna 2 significa que nenhum parmetro de tamanho satisfaz a faixa especificada; e se retorna 3 porque nenhum parmetro de tamanho satisfaz a preciso e a faixa especificadas. A seguir tem-se alguns exemplos de uso desta funo intrnseca
program usando_kind implicit none integer :: valor_kind valor_kind = selected_real_kind(p=2,r=10) valor_kind = selected_real_kind(p=5,r=35) valor_kind = selected_real_kind(p=7) valor_kind = selected_real_kind(r =30) valor_kind = selected_real_kind(p=20,r=350) stop end program usando_kind
No programa acima o primeiro valor_kind ser 4, porque em um PC (onde estou testando os programas) os dados reais em preciso simples usa 4 bytes ou 32 bits; o segundo valor de valor_kind ser tambm 4; o terceiro ser 8 porque uma varivel de 4 bytes prov uma preciso de apenas 6.8 casas decimais, ou seja abaixo de 7; no quarto caso ser 4 porque a faixa que uma variveis com 4 bytes estar na faixa de at 38; e por fim o ltimo valor ser 3 indicando que nenhum parmetro de tamanho atende aquela especificao.
77
Boa Prtica
Use a funo intrnseca SELECTED_REAL_KIND para determinar o parmetro de tamanho da varivel real desejada. A funo retornar o valor mais adequado para atender a necessidades, e tornar o programa mais portvel.
Existem ainda outras funes intrnsecas relacionadas ao parmetro de tamanho (kind), mostradas na Tabela 7.2 Tabela 7.2-Funes intrnsecas comuns relacionadas ao parmetro de tamanho (kind) Funo intrnseca Descrio selected_real_kind(p,r) Retorna o menor valor do parmetro de tamanho com um mnimo de p casas decimais e uma faixa 10r selected_int_kind(r) Retorna o menor valor do parmetro de tamanho com um range 10r kind(x) Retorna o parmetro de tamanho da varivel x, onde x uma constante ou varivel de qualquer tipo. precision(x) Retorna a preciso decimal de x, onde x real ou complexo. range(x) Retorna o expoente decimal de x, onde inteiro, real ou complexo
7.1.4. Preciso dupla em funes intrnsecas
Todas as funes genricas do Fortran 90/95 que operam com preciso simples, suportam tambm valores reais com preciso dupla. Se o argumento for preciso simples o retorno ser preciso simples, se o argumento for preciso dupla a resposta ser tambm preciso dupla.
7.2 Outros tamanhos (kinds) de dados para variveis inteiras
No caso de dados do tipo inteiro a idia geral totalmente semelhante quela dos dados do tipo real. No caso dos inteiros mais simples, pois necessita-se especificar apenas a faixa que o nmero deve satisfazer. Na Tabela 7.3 tm-se os parmetros de tamanho para dados do tipo inteiro para alguns processadores. Tabela 7.3-Parmetros de tamanho (kind) e tamanho de variveis inteiras em compiladores Fortran 90 Computador/Compilador int8 int16 int32 int48 int64 Cray T90 Supercomputer/CF90 NA NA 1,2,4 6* 8 DEC/DEC Fortran90 1 2 4* NA 8 PC/Lahey Fortran 90 1 2 4* NA NA PC/Microsoft Powerstation 4.0 1 2 4* NA NA PC/NAGWare FTN90 1 2 4* NA NA *Valores default
7.3 O tipo de dados COMPLEX
Praticamente tudo que se disse at o momento sobre reais aplica-se tambm aos do tipo complexo. Um complexo em realidade um dupla de dados reais que
Verso preliminar. Escrito em 12/01/99. Impresso em 08/25/05
78
onde na dupla (a,b) o real da esquerda a componente real do dado complexo e b a parte imaginria.
7.3.1 Aritmtica mista com complexos
Quando um complexo vai sofrer uma operao com um nmero no complexo o programa converte o valor do dado no complexo para complexo e executa a operao.
7.3.2 Utilizando operadores relacionais com complexos
possvel comparar dois dados complexos usando o operador relacional == e pode-se tambm verificar se so diferentes usando o operador /=. Os outros operadores relacionais >, <, >= e <= no podem ser usados com complexos.
7.4 Tipos de dados derivados
Os tipos de dados bsicos do Fortran so: inteiros, reais, caracteres, lgicos e complexos. O Fortran permite ao usurio definir os seus prprios tipos de dados, so os chamados tipos derivados de dados. Eles so derivados porque devem ser construdos utilizando aqueles tipos bsicos mencionados acima e eventualmente outros tipos de dados derivados j definidos. Uma das vantagens dos dados derivados o encapsulamento, isto os componentes internos do dados derivado devem ser acessado explecitamente. Um dado do tipo derivado uma maneira conveniente de agrupar um conjunto de informaes, de uma maneira semelhante definio de um array como um conjunto de elementos. Porm a sintaxe diferente, e cada elemento referenciado por um nome ao invs de por um nmero. Um dado derivado definido como uma seqncia de declaraes precedidas da declarao TYPE e completado com a declarao END TYPE. A forma geral de declarao de um dado derivado
type :: nome_do_tipo componentes_da_declaracao end type nome_do_tipo
79
Uma vez que uma determinado dado derivado esteja definido possvel utiliz-lo e produzir as seguintes definies
type (frutos_no_cesto) :: cesto1, cesto2, cesto_na_prateleira type (frutos_no_cesto), dimension(20) :: cesto_grande, cesto_pequeno
Um dado derivado tambm chamado de estrutura. Uma estrutura pode ser inicializada utilizando um construtor de estruturas, de um forma semelhante ao construtor de arrays, como segue
cesto1 = cesto_de_frutas(12,9,0,2,5) cesto2 = cesto_de_frutas(0,24,3,0,6) Na primeira linha de cdigo logo acima, o construtor da estrutura cesto1 ir atribuir 12 ao numero_bananas, 9 ao numero_laranjas, 0 ao numero_cajus, 2 ao numero_mangas, e 5 ao numero_goiabas. Uma estrutura pode ser usada como elemento em outra estrutura. 7.4.1 Acessando elementos de uma estrutura
Os componentes numricos de uma estrutura podem ser usados de acordo com as operaes cada tipo suporta. O mesmo vale os componentes do tipo caracter. Como estes elementos esto encapsulados dentro da estrutura, para acess-los ser necessrio usar o seletor de componentes (component selector) No exemplo dos cestos de frutos mostrados acima pode-se atribuir valor a um componente da estrutura da seguinte maneira
cesto1%numero_bananas = 13
ou
cesto1.numero_bananas = 13
estas duas formas acima, usando % ou usando . so equivalentes. Para somar o nmero de goiabas no cesto1 e no cesto2, o procedimento faz-se
quantidade_goiabas = cesto1.goiabas + cesto2.goiabas
Os tipos de dados derivados podem ser definidos dentro de mdulos, evitando ter que defini-los em todas as unidades de programa. Abaixo apresenta-se a definio de uma estrutura chamada caixa que tem trs componentes: largura, profundidade e altura.
program estrutura_caixa
80
81
At o momento todos os subprogramas que definimos, funes, subrotinas e mdulos, eram do tipo externo. Vamos ver agora como construir os chamados subprogramas internos. O Fortran 90/95 suporta os subprogramas internos, que so funes e subrotinas que ficam dentro do programa principal, ou de algum outro subprograma. Os subprogramas internos so compilados juntamente com a compilao do programa hospedeiro, e podem ser invocadas apenas por ele. O local no programa em que fica um subprograma interno logo aps a ltima declarao executvel do programa, e antes das declaraes STOP e END PROGRAM. Separando os subprogramas internos do restante do cdigo deve-se usar apenas a palavra CONTAINS. De outra forma, todos os subprogramas internos devero estar entre a declarao CONTAINS e a finalizao do programa. O nome de um subprograma interno no pode ser passado como argumento de linha de comando a outro subprograma. Um subprograma interno recebe todas as variveis e constantes definidas no seu hospedeiro, em outras palavras todas as variveis e constantes do programa hospedeiro so globais para os seus subprogramas internos. Quando define-se uma varivel no subprograma interno, que tenha o mesmo nome de uma varivel do programa hospedeiro, ento esta varivel passa a ter um carter local, e por conseguinte no consegue acessar o valor contido na varivel do mesmo nome no programa hospedeiro.
8.2 Escopo de variveis e constantes
Os escopos possveis de todos os tipos de objetos (inteiro, real, lgico, complexo, caracter, derivado) so: global, local e declarao. Objetos globais so aqueles cuja definio atinge todas as instncias do programa (programa principal e subprogramas), consequentemente os nomes destes objetos devem ser nicos em todas as unidades do programa. Os nicos objetos que gozam desta propriedade so o nome do programa principal, os nomes das funes, os das subrotinas, e os dos mdulos. Utilizando-se mdulos tambm pode-se produzir variveis que tenham um escopo global, quando todas as unidades de programa referenciarem um determinado mdulos de compartilhamento de dados. Objetos locais so definidos apenas dentro de algumas unidades do programa, tambm chamadas de unidades de escopo. Alguns exemplos so: programas, funes, subrotinas, e mdulos. O nome de um objeto de ser nico dentro de sua unidade de escopo, porm o mesmo nome pode ser usado para outra varivel em outra unidade de escopo. Um objeto tambm pode ter como escopo declaraes do tipo DO implcito em construtores de arrays, abaixo tem-se um exemplo de onde pode ocorrer este tipo de escopo de variveis
Verso preliminar. Escrito em 12/01/99. Impresso em 08/25/05
82
Abaixo mostra-se um programa exemplo que apresenta todos os tipos de escopo: global, local e de declaraes. module global implicit none save integer, parameter :: j=5 end module global
program escopo !Objetivo: Demonstrar os tipos de escopo: global, local e declarao ! !programmer: joao batista aparecido ilha solteira, 14 dezembro 19999 ! !Declarando variveis use global implicit none integer, parameter :: k = 6 integer :: i real, dimension(k) :: a do i=1,k a = (/(i*i, i =1,k)/) end do call sub(k,a) stop end program escopo subroutine sub(k,a) use global implicit none integer :: k, i real, dimension(k) :: a do i = 1, j a(i) = sin(a(i)) end do return end subroutine sub
No programa acima alm do nome do programa, da subrotina e do mdulo, tambm o inteiro j definido no mdulo tm escopo global. No programa principal tem-se um lao de DO em que o ndice (i) vai de 1 a 6 (k), este lao inicializa o vetor a seis vezes, e dentro do lao tem o lao de DO implcito, cuja varivel de controle tambm (i) que vai atribuir valores a cada posio do array, note que no ocorre erro de compilao, nem erro de link, nem erro de execuo, isto porque a varivel (i) do lao interno tem escopo apenas dentro do
Verso preliminar. Escrito em 12/01/99. Impresso em 08/25/05
83
As funes e subrotinas comuns do Fortran 90/95 no podem invocar a si mesmas, quer seja diretamente ou indiretamente. Significa dizer que os subprogramas comuns do Fortran no so recursivos. Alguns tipos de problemas podem ser resolvidos mais facilmente se determinada funo ou subrotina recursiva. O Fortran 90/95 suporta funes e subrotinas recursivas desde que tenham o atributo RECURSIVE, colocado antes da palavra FUNCTION ou SUBROUTINE. Um exemplo clssico de uso de subrotina recursiva para calcular o fatorial de um inteiro n. Abaixo apresenta-se um programa que faz o cmputo do fatorial de um inteiro n, utilizando-se de um subrotina recursiva.
program recursivo implicit none integer :: n=5, valor call fatorial(n,valor) stop end program recursivo recursive subroutine fatorial(i,x) implicit none integer :: i, x, aux if (i>=1) then call fatorial(i-1,aux) x = i*aux else x=1 end if return end subroutine fatorial
No caso de definio de funes recursivas existe um complicao extra. Como j vimos anteriormente para invocar uma funo necessrio que ela esteja em uma
84
Abaixo tem-se uma nova verso do programa para calcular o fatorial de n, agora utilizando uma funo recursiva
program recursivo_2 implicit none integer :: n=5, valor, fatorial valor = fatorial(n) stop end program recursivo_2 recursive function fatorial(i) result(retorno) implicit none integer :: i, retorno if (i>=1) then retorno = i*fatorial(i-1) else retorno = 1 end if return end function fatorial
J foi dito vrias vezes que a quantidade, o tipo e o tamanho dos argumentos de uma funo ou subrotina devem coincidir onde ela invocada e com sua definio. Caso contrrio haver algum tipo de erro, no de compilao, na lincagem ou na execuo. Existe situaes nas quais possvel modificar a ordem em que os argumentos aparecem na chamada e no subprograma, e mesmo assim o programa operar corretamente. Esta tcnica a ser descrita s funcionar se o subprograma tiver uma interface explicita. Como j foi dito anteriormente uma das maneiras de prover uma interface explicita colocar o subprograma dentro de um mdulo. A outra forma, que veremos mais adiantes, consiste em prover explicitamente blocos de interface. Se o subprograma possuir uma interface explicita ento possvel usar argumentos nomeados, no programa que est invocando o subprograma, de tal forma que seja possvel ao programa saber onde est cada argumento da lista de argumentos e fazer a
Verso preliminar. Escrito em 12/01/99. Impresso em 08/25/05
85
onde palavra_chave o nome do argumento que est sendo associado com o argumento verdadeiro. Se a invocao do subprograma usa palavras-chave, ento os argumentos na chamada podem ser arranjados em qualquer ordem porque as palavras-chave auxiliam o compilador a saber qual argumento corresponde a qual. Vamos ilustrar estas idias construindo um programa que chama uma subrotina duas vezes. Na primeira chamada os argumentos esto colocados na lista de argumentos sem usar palavras-chave, e na Segunda vez est com os argumentos embaralhados porm usando palavras-chave. O resultando em os ambos os casos ser o mesmo.
module sub contains subroutine sub1(r,s,t,u) implicit none real :: r,s,t,u u=r+s+t return end subroutine sub1 end module sub program argumento_embaralhado use sub implicit none real :: a =1.0, b =2.0, c=3.0, d call sub1(a,b,c,d) write(*,*) d call sub1(u=d, s=b, r=a, t=c) write(*,*) d stop end program argumento_embaralhado
Esta prtica um pouco mais segura porm necessrio um gasto maior de tempo escrevendo os argumentos da chamada do subprograma em uma forma mais complexa. A principal conseqncia desta tcnica no entanto outra. Com este tipo de declarao ser possvel ter argumentos opcionais. Argumentos opcionais so aqueles que podem ou no aparecer na lista de argumentos e o subprograma no acusar erro de compilao e a execuo continuar, alm do mais o compilador ter condies de
86
87
Conforme j foi dito anteriormente desejvel que o programador coloque as subrotinas e funes dentro de um mdulo, de tal forma que tenham automaticamente uma interface explicita. No entanto nem sempre possvel colocar-se estas informaes dentro de um mdulo. So vrias as situaes: imagine um conjunto de rotinas que no se tm o cdigo fonte para fazer as modificaes; ou uma biblioteca antiga e muito grande que ficaria muito cara re-escreve-la novamente; ou ainda uma biblioteca escrita em outra linguagem. Desta forma til alguma ferramenta que permita a construo de interfaces explicitas sem necessariamente ter que mexer no cdigo original. A maneira de se fazer isto utilizando blocos de interface.
8.5.1. Criando blocos de interface
Os blocos de interface so criados no programa que invoca os subprogramas, colocados na seo de declarao de variveis. Uma interface criada duplicando as informaes das declaraes de atributo de todas as variveis da lista de argumentos, e colocadas entre as declaraes INTERFACE e END INTERFACE. A forma geral para criar um bloco de interface
interface subroutine nome_da_subrotina (lista_argumentos) declaracoes_de_atributos_dos_argumentos end subroutine nome_da_subroutine function nome_da_function (lista_argumentos) declaracoes_de_atributos_dos_argumentos end function nome_da_function end interface
A forma mostra acima apresenta apenas um funo e uma subrotina, mas podem ser empilhadas tantas quantas forem necessrias. Estas informaes contidas na interface so suficientes para que o compilador reconhea a quantidade, tipo e tamanho dos argumentos.
program interface_explicita implicit none !Definindo o bloco de interface interface subroutine sub1(r,s,t,u) implicit none
88
Estes programa usando uma interface explicita produzida via um bloco de interface produz exatamente o mesmo resultado que outro mostrado mais acima que tinha uma interface explicita produzida pelo fato da subrotina estar imersa em um mdulo. Os parmetros listados em um bloco de interface deve coincidir em quantidade, tipo, ordem e tamanho com aqueles do subprograma que se est interfaceando, mas no necessrio que o nome seja o mesmo.
Boa Prtica
Sempre que possvel coloque as subrotinas e funes dentro de um mdulo de tal forma que tenham um interface explicita, sem utilizar blocos de interface.
89
Boa Prtica
8.6 Subprogramas genricos
Se for necessrio criar vrios blocos de interface, ento coloque os dentro de mdulo e as acesse via uma declarao USE.
O Fortran 90/95 inclui entre suas funes intrnsecas, funes de uso genrico e funes de uso especfico. As genricas operam sobre dados de diferentes naturezas, por exemplo: inteiros, reais e arrays de inteiros e reais. As especficas operam apenas sobre um determinado tipo de dado.
8.6.1 Subprogramas genricos definidas pelo programador
Viemos at o momento tratando com subprogramas (definidos pelo programador) apenas do tipo especfico onde fixa-se o tipo de varivel, e o subprograma s funciona para aquele tipo. O Fortran 90/95 suporta funes genricas definidas pelo programador. Suponha que temos vrias rotinas capazes de somar dois dados de diferentes tipos e tamanhos, e queremos produzir apenas um rotina que seja capaz de operar sobre todos os tipos e retornar um resultado compatvel. Se definirmos um nome genrico para a interface, diferente do nome dos subprogramas dentro da interface, ento o compilador Fortran 90/95 ser capaz de entender que todos os subprogramas contidos dentro daquela interface so verses especiais do subprograma genrico que se quer construir. A seguir apresenta-se um programa que adiciona dois nmeros reais ou inteiros e de comprimentos diferentes.
module tipo_dado !Mdulo para compartilhar os parmetros de tamanhos das variveis save integer, parameter :: curto = 2, longo = 4, simples = 4, duplo =8 end module tipo_dado program soma_generica !Objetivo: Mostrar como se define uma funo genrica ! !programador: joao batista aparecido, ilha solteira, 15 de dezembro 1999 ! use tipo_dado implicit none !Definio da interface genrica (soma) interface soma subroutine soma1(a,b,c) !Interface da subrotina que soma inteiros curtos use tipo_dado implicit none integer(curto), intent(in) :: a,b integer(curto), intent(out) :: c end subroutine soma1 subroutine soma2(a,b,c) !Interface da subrotina que soma inteiros longos use tipo_dado implicit none integer(longo), intent(in) :: a,b
90
integer(curto) :: i=1,j=5,k integer(longo) :: m=2,n=9,o real(simples) :: x=1.e0, y=-1.e0/3.e0, z real(duplo) :: r=1.d0, s=-1.d0/3.d0, t call soma(i,j,k) call soma(m,n,o) call soma(x,y,z) call soma(r,s,t) stop end program soma_generica subroutine soma1(a,b,c) !Subrotina que soma dois inteiros curtos use tipo_dado implicit none integer(curto), intent(in) :: a,b integer(curto), intent(out) :: c c = a+ b return end subroutine soma1 subroutine soma2(a,b,c) !Subrotina que soma dois inteiros longos use tipo_dado implicit none integer(longo), intent(in) :: a,b integer(longo), intent(out) :: c c = a+ b return end subroutine soma2 subroutine soma3(a,b,c) !Subrotina que soma dois reais simples use tipo_dado
91
Boa Prtica
Use blocos de interface genricos para definir subprogramas que podem operam com tipos de dados variveis. Subprogramas genricos facilitam operaes com diferentes tipos de dados.
No item anterior mostrou-se como criar um subprograma genrico usando interfaces explicitas via bloco de interface. Agora mostra-se como fazer a mesma coisa porm usando mdulo. A dificuldade em se fazer isto em um mdulo que se os subprogramas j est no mdulo, eles automaticamente j tem cada um uma interface, ento como definir um interface mais genrica se o conceito de mdulo probe a definio de uma interface explicitamente dentro dele uma vez que ela j gerada automaticamente. O Fortran apresenta uma declarao especial para efetuar esta tarefa a declarao MODULE PROCEDURE, que pode ser usada em bloco de interface genrica. A forma desta declarao
module procedure lista_nome_procedure
onde procedure lista_nome_procedure a lista de subprogramas que compe funo genrica. Para o caso mostrado no programa acima, a interface genrica para a subrotina soma deve ser da seguinte forma
interface soma module procedure soma1 module procedure soma2 module procedure soma3 module procedure soma4 end interface
92
Quando definimos no captulo anterior os tipos de dados derivados conseguimos apenas atribuir valores s componentes dos dados derivados. Porm no foi possvel fazer operaes sobre as variveis como um todo. As operaes bsicas dos operadores aritmticos binrios e unrios no funcionam para dados derivados. Isto porque o programador deve fornecer informaes suficientes, ao compilador, para se construir uma lgebra prpria de cada tipo especfico de dado derivado. O Fortran 90/95 uma linguagem extensvel de tal forma que o programador pode ir adicionando novas aspectos para acomodar tipos especiais de dados. J vimos como definir tipos novos de dados, vamos ver agora como definir novos operadores. Ou de outra forma como definir operadores para estes novos tipos de dados. Assim, como a devida imaginao possvel dar novos sentidos para adio, subtrao, multiplicao, diviso e comparaes de dados derivados. Como isto pode ser feito? O primeiro passo construir um subprograma que execute o desejado e coloc-lo em um mdulo. O segundo passo associar este subprograma com um operador (smbolo) usando um bloco de interface de operador. A forma desta interface
interface operator (simbolo_do_operador) module procedure nome_da_function end interface
onde simbolo_do_operador qualquer um dos operadores intrnsecos (+,-,*,/,>,< etc.) ou qualquer smbolo definido pelo programador. Um operador definido pelo programador pode ter at 31 caracteres cercado por um ponto antes e um ponto depois, tal como .DIVIDIR. Mais de um tipo de subprograma pode estar associado a um dado operador, mas ser necessrio que estes subprogramas possam ser distinguidos uns dos outros atravs da anlise dos tipos e tamanhos de seus argumentos. Quando o compilador encontra um operador, ento procura pelo correspondente subprograma. Se o operador tem dois argumentos ento um operador binrio, se tem apenas um unrio. Uma vez definido o operador ser tratado como uma referncia para a funo. Se o operador sendo definido pela interface utiliza os smbolos dos operadores intrnsecos (+,-,*,/, etc.) ento tem-se alguns restries adicionais: 1) No se pode mudar o sentido dos operadores intrnsecos quando estes operam sobre os tipos bsicos de dados (inteiro, real, lgico, complexo e caracter). No entanto estes operadores podem ser estendidos em outros sentidos para quando estiverem operando com dados derivados; 2) O nmero de argumentos em uma funo deve ser consistente com suas natureza, ou seja em uma adio s se pode somar dois nmeros de cada vez. Qualquer extenso do sentido da soma deve atender a este requisito.
93
Para o operador de atribuio a interface deve ter uma subrotina ao invs de uma function. A subrotina dever ter dois argumentos O primeiro o argumento de sada e deve ter o atributo intent(out). O segundo o argumento de entrada e deve ter o atributo intent(in). O argumento da esquerda corresponde ao lado esquerdo da atribuio e o direito ao da direita.
Use blocos de interface de operadores e de atribuio para criar novos operadores ou estender o significado dos j existentes para trabalhar com tipos de dados derivados. Uma vez tendo definido estes operadores fica muito mais fcil operar com este tipo de dados..
Boa Prtica
Abaixo apresenta-se uma aplicao executar operaes sobre um tipo de dado derivado que um vetor com trs componentes.
MODULE vectors !Purpose: !To define a derived data type called vector and the !operations that can be performed on it. The module !defines eight operations that can be performed on vectors: ! ! 1.Creation from a real array = ! 2.Conversion to a real array = ! 3.Vector addition + ! 4.Vector subtraction ! 5.Vector-scalar multiplication (4 cases) * ! 6.Vector-scalar division (2 cases) / ! 7.Dot product .dot. ! 8.Cross product * ! !IL contains a total of 12 procedures to implement those !operations: array_to_vector, vector_to_array, vector_add, !vector_subtract, vector_times_real, real_times_vector, !vector_times_int, int_times_vector, vector_div_real, !vector_div_int, dot _product, and cross _product. ! IMPLICIT NONE ! Declare vector data types: TYPE :: vector REAL :: x REAL :: y REAL :: z END TYPE
94
95
96
97
At o momento todos os dados que foram colocados nos mdulos estavam totalmente disponveis e acessveis. No entanto as vezes no h interesse que determinados dados dos interior de um mdulo esteja disponvel fora do mdulo. Ento a questo : Como esconder alguns dados do interior de um mdulo? O Fortran dois atributos para tratar esta questo, o PUBLIC e o PRIVATE. Se declararmos uma varivel interna de um mdulo de PRIVATE, ela ficar totalmente indisponvel fora do ambiente do mdulo. Por outro lado ela continuar totalmente disponvel no interior do mdulo. O atributo PUBLIC o default e torna uma varivel totalmente disponvel, quer seja internamente ao mdulo quer seja externamente. Abaixo tem-se alguns exemplos de como efetuar esta declarao
integer, private :: conta real, public :: tension type (caixa), private :: caixa_sapato aconselhvel esconder qualquer dado ou subprograma de um mdulo que no precisem ser acessados de fora. A melhor maneira para ser fazer isto incluir o atribute PRIVATE em cada mdulo, e listar os itens especficos que se quer deixar visveis em uma declarao especfica com o atributo PUBLIC.
Boa Prtica
Quando um programa acessa um mdulo todas as informaes ali contidas ficam disponveis para os programa que est acessando, exceto aquelas protegidas com o atributo PRIVATE. No entanto nem todas as informaes disponveis sero usadas, assim interessante que se acesse apenas as que forem necessrias. Para restringir este acesso pode-se adicionar a especificao ONLY declarao USE. A forma de se fazer
use nome_do_modulo, only : lista_only
onde nome_do_modulo o nome do mdulo e lista_only a lista dos itens do mdulo a serem usadas. Nesta lista o itens so separados por vrgula. Tambm possvel renomear dados ou subprogramas de um mdulo quando usando a declarao USE. Uma das razes para renomear um item a existncia em algum local do programa de uma outra varivel com o mesmo nome. Fazendo esta renomeao no vai haver conflito de nomes. Outra razo surge quando um dado que usado com muita freqncia e tem um nome muito grande, pode-se ento renome-lo com um nome pequeno e economizar em tempo de digitao.
Verso preliminar. Escrito em 12/01/99. Impresso em 08/25/05
98
Na declarao abaixo
use data_fit, only : sp_real_least_square_fit => lsqfit
est utilizando-se um mdulo chamado data_fit. De todos os dados e subprogramas que este mdulo tem, utiliza-se apenas o chamado sp_real_least_square_fit que est sendo renomeado, localmente, para lsqfit.
99
Uma varivel ponteiro (pointer) quando possui o atributo POINTER na sua declarao de varivel, ou atravs de um declarao POINTER. Adiante apresenta-se alguns exemplos da sintaxe para definir ponteiros
real, pointer :: p_x pointer :: p_x
Como foi dito anteriormente um ponteiro no tm valor prprio mas guarda o endereo de memria de uma outra varivel. Uma varivel ponteiro dever ter o mesmo tipo declarado da varivel para qual aponta. De outra forma, um ponteiro s poder apontar para uma varivel que seja do seu mesmo tipo. Pode existir ponteiros apontando para variveis do tipo derivado. Ponteiros tambm podem apontar para arrays, neste caso na declarao do ponteiro estar explicito o rank do array mas no a sua extenso, de qualquer forma o ponteiro aponta para o primeiro elemento do array. Algumas maneiras de definir ponteiros para arrays so
integer, dimension(:), pointer :: p_y real, pointer, dimension(:,:) :: p_w pointer, real, dimension(:,:,:) :: p_z Os ponteiros acima apontam para arrays rank 1, rank 2 e rank 3, respectivamente. Verso preliminar. Escrito em 12/01/99. Impresso em 08/25/05
100
Um ponteiro pode ser associado a uma varivel alvo atravs de uma declarao de atribuio de ponteiro. Esta declarao tem o seguinte formato
ponteiro => alvo
onde ponteiro o nome da varivel ponteiro que ir conter o endereo do alvo, e alvo a varivel apontada pelo ponteiro. Depois que um ponteiro foi associado a uma varivel alvo, qualquer referncia ao ponteiro em realidade uma referncia varivel alvo. Se um ponteiro est associado a uma varivel alvo e ento novamente associado a uma segunda varivel alvo, a primeira associao ser desfeita. Em outras palavras um ponteiro no pode apontar para dois alvos simultaneamente, no entanto um alvo pode ser apontado por um, dois ou mais ponteiros. Quando um alvo est sendo apontado por dois ponteiros e um dos ponteiros movido, o outro ponteiro no ser modificado. Um valor que est armazenado em uma varivel alvo pode ser obtido referenciado a varivel alvo ou ento referenciado o ponteiro que est apontado para ela. Um ponteiro pode apontar para outro ponteiro, neste caso em realidade os dois ponteiros estaro apontando para o mesmo alvo.
Boa Prtica
Uma boa conduta na definio de variveis do tipo ponteiro colocar um p no incio do nome da varivel para se ter uma distino clara quando se l um programa de Quais variveis so ponteiros e quais no so.
A situao da associao de um ponteiro e um alvo indica se um dado ponteiro est associado a um dado alvo. possvel trs situaes: associado (associated), indefinido (undefined) e desassociado (disassociated). Quando um ponteiro criado em uma declarao de tipo sua situao indefinido, quando associado a um alvo a situao ento associado, e quando desassociado do alvo sua situao torna-se desassociado. J vimos como declarar um ponteiro e como associar um ponteiro a um alvo, resta vermos como desassociar um ponteiro de seu alvo. Para tal necessrio apenas executar uma declarao NULLIFY com o seguinte formato
nullify(lista_de_ponteiros)
101
que responder se ponteiro j est associado ou no. A Segunda forma de uso desta funo com dois argumentos
variavel_logica = associated(ponteiro, alvo)
Quando um ponteiro aparece em uma expresso aritmtica, no lugar de um valor numrico ento o valor da varivel para a qual ele est apontando usado. Quando se escreve
p_x = p_y
o valor que est na varivel alvo apontada por p_y atribuda posio de memria da varivel alvo apontada por p_x. Adicionalmente, se escrevemos
p_x => p_y
significa que estamos apontando o ponteiro p_x para o ponteiro p_y, assim p_x ficar apontando para a mesma posio de memria que p_y, ou seja a varivel alvo de p_y. Abaixo apresenta-se um programa que utiliza ponteiros para trocar a posio de duas matrizes
program usando_pointer !Objetivo: Mostrar o uso de ponteiros ! !programador: joo batista aparecido ilha solteira, 16 de dezembro, 1999 ! implicit none integer, parameter :: i_max = 100, j_max =100 integer :: i,j real :: aux
102
!Fazendo o swap das matrizes da forma clssica do i = 1, i_max do j = 1, j_max aux = x(i,j) x(i,j) = y(i,j) y(i,j) = aux end do end do !Fazendo o swap das matrizes usando ponteiros p_aux => p_x p_x => p_y p_y => p_aux stop end program usando_pointer
Um dos aspectos mais poderosos do uso de ponteiros em alocao dinmica que ele permite no s a criao de espaos na memria como tambm depois de criados aumentar ou diminuir estes espaos sem destruir os dados j armazenados. A maneira de se fazer isto parecida com aquela j vista usando arrays alocveis. Espaos em memria so alocados usando a declarao ALLOCATE, e desalocados usando DEALLOCATE. Neste a declarao ALLOCATE fica da seguinte forma
allocate(ponteiro_1(forma_do_ponteiro_1),,stat = status)
onde ponteiro_1 o nome do primeiro ponteiro a ser alocado, e assim por diante, forma_do_ponteiro_1 a forma, ou seja o rank e a extenso em cada direo que possui a estrutura para a qual o ponteiro vai apontar, e status o resultado para operao, se a alocao for bem sucedida seu valor zero, caso contrrio seu valor ser dependente de zero e depender da implementao de cada compilador, sendo necessrio consultar o manual do fabricante. A operao descrita acima criar um ponteiro que apontar para um conjunto de dados sem nome. Devido ao fato deste objeto no ter nome ele poder ser acessado apenas via ponteiro. Depois que a rotina acima executada com sucesso a situao do ponteiro ser associado. Se o ponteiro (ou ponteiros), que est associado a este objeto sem nome, for nulificado ou redirecionado para outro objeto, aquele objeto estar perdido para sempre. Porm ainda ocupando as posies de memria, criando o
103
Quando um espao de memria desalocado usando DEALLOCATE automaticamente o ponteiro que apontava para aquele objeto nulificado. Logo abaixo apresenta-se a verso do programa anterior, agora usando alocao dinmica de memria
program usando_pointer !Objetivo: Mostrar alocao dinmica usando ponteiros ! !programador: joo batista aparecido ilha solteira, 16 de dezembro, 1999 ! implicit none integer, parameter :: i_max = 100, j_max =100 integer :: status real, pointer, dimension(:,:) :: p_x, p_y, p_aux !Definindo os ponteiros nullify(p_x,p_y,p_aux) !Nulificando os ponteiros allocate(p_x(i_max,j_max),p_y(i_max,j_max),stat = status) !Alocando memria p_x = 1.0 p_y = 5.0 !Inicializando o array para onde aponta p_x !Inicializando o array para onde aponta p_y
!Fazendo o swap das matrizes usando ponteiros p_aux => p_x p_x => p_y p_y => p_aux deallocate(p_x, stat=status) !Desalocando memria deallocate(p_y, stat=status) !Desalocando memria deallocate(p_aux, stat=status)!Desalocando memria stop end program usando_pointer
Pode-se colocar ponteiros dentro de estruturas derivadas de dados. Assim seja a seguinte estrutura derivada de dados
type :: real_value real :: value type(real_value), pointer :: p end type
104
! Description of change ! ===================== ! original code IMPLICIT NONE ! Derived data type to store real values in TYPE :: real_value REAL :: value TYPE (real_value), POINTER :: p END TYPE ! List of variables: TYPE (real_value), POINTER :: head ! Pointer to head of list CHARACTER(len=20) :: filename ! Input data file name INTEGER :: nvals = 0 ! Number of data read TYPE (real_value), POINTER :: ptr ! Temporary pointer TYPE (real_value), POINTER :: tail ! Pointer to tail of list INTEGER :: istat ! status: 0 for success REAL :: temp, aux ! Temporary variable ! Get the name of the file containing the input data. WRITE (*,*) 'Enter the file name with the data to be read: ' READ (*,'(A20)') filename ! open input data file. OPEN ( UNIT=9, FILE=filename, STATUS='OLD', ACTION='READ',IOSTAT = istat )
105
106
A estrutura derivada de dados utilizada acima pode ser estendida para conter no seu interior vrios valores reais, inteiros, lgicos, caracteres, complexos, assim uma extenso nesta direo seria, por exemplo, o que segue
type :: various_values complex :: c1 integer :: int1, int2 real :: value1, value2, value3 character(20) :: nome1, nome2 logical :: logic1, logic2, logic3 type(various_values), pointer :: p end type
Um outro tipo de extenso, na estrutura de dados acima, incorporar mais de uma ponteiro. Um exemplo bem conhecido na literatura a arvore binria que acrescenta mais um ponteiro ao tipo derivado de dados. Assim cada elemento na estrutura global estar apontando para nenhum, um ou dois outros elementos, formando assim uma arvore binria. Uma estrutura razoavelmente genrica de uma rvore binria seria
type :: various_values_and_two_pointers complex :: c1 integer :: int1, int2 real :: value1, value2, value3 character(20) :: nome1, nome2 logical :: logic1, logic2, logic3 type(various_values_and_two_pointers), pointer :: p1, p2 end type
Imagine-se uma arvore binria para armazenar e disponibilizar os primeiro, segundo e terceiro nomes de uma pessoa bem como o seu respectivo telefone. A seguir o programa que usa esta estrutura de dados
MODULE btree ! ! Purpose: ! To define the derived data type used as a node in the ! binary tree, and to define the operations >, <. and == ! for this data type. This module also contains the ! subroutines to add a node to the tree, write out the ! values in the tree, and find a value in the tree. ! ! Record of revisions: ! Date Programmer Description of change ! ==== ========== ===================== ! 02/04/96 S. J. Chapman Original code ! IMPLICIT NONE ! Restrict access to module contents. PRIVATE
107
108
109
110
111
! The file was opened successfully, allocate space for each ! node, read the data into that node, and insert it into the ! binary tree. input: DO ALLOCATE (temp,STAT=istat) ! Allocate node NULLIFY ( temp%before, temp%after) ! Nullify pointers READ (9, 100, IOSTAT=istat) temp%last, temp%first, & temp%mi, temp%phone ! Read data 100 FORMAT (A10,1X,A10,1X,A1,1X,A16) IF ( istat /= 0 ) EXIT input ! Exit on end of data CALL add_node(root, temp) ! Add to binary tree END DO input ! Now, write out the sorted data. WRITE (*,'(/,1X,A)') 'The sorted data list is: ' CALL write_node(root)
112
113
Uma varivel ponteiro (pointer) quando possui o atributo POINTER na sua declarao de varivel, ou atravs de um declarao POINTER. Adiante apresenta-se alguns exemplos da sintaxe para definir ponteiros
real, pointer :: p_x pointer :: p_x
Como foi dito anteriormente um ponteiro no tm valor prprio mas guarda o endereo de memria de uma outra varivel. Uma varivel ponteiro dever ter o mesmo tipo declarado da varivel para qual aponta. De outra forma, um ponteiro s poder apontar para uma varivel que seja do seu mesmo tipo. Pode existir ponteiros apontando para variveis do tipo derivado. Ponteiros tambm podem apontar para arrays, neste caso na declarao do ponteiro estar explicito o rank do array mas no a sua extenso, de qualquer forma o ponteiro aponta para o primeiro elemento do array. Algumas maneiras de definir ponteiros para arrays so
integer, dimension(:), pointer :: p_y real, pointer, dimension(:,:) :: p_w pointer, real, dimension(:,:,:) :: p_z Os ponteiros acima apontam para arrays rank 1, rank 2 e rank 3, respectivamente. Verso preliminar. Escrito em 12/01/99. Impresso em 08/25/05
114
Um ponteiro pode ser associado a uma varivel alvo atravs de uma declarao de atribuio de ponteiro. Esta declarao tem o seguinte formato
ponteiro => alvo
onde ponteiro o nome da varivel ponteiro que ir conter o endereo do alvo, e alvo a varivel apontada pelo ponteiro. Depois que um ponteiro foi associado a uma varivel alvo, qualquer referncia ao ponteiro em realidade uma referncia varivel alvo. Se um ponteiro est associado a uma varivel alvo e ento novamente associado a uma segunda varivel alvo, a primeira associao ser desfeita. Em outras palavras um ponteiro no pode apontar para dois alvos simultaneamente, no entanto um alvo pode ser apontado por um, dois ou mais ponteiros. Quando um alvo est sendo apontado por dois ponteiros e um dos ponteiros movido, o outro ponteiro no ser modificado. Um valor que est armazenado em uma varivel alvo pode ser obtido referenciado a varivel alvo ou ento referenciado o ponteiro que est apontado para ela. Um ponteiro pode apontar para outro ponteiro, neste caso em realidade os dois ponteiros estaro apontando para o mesmo alvo.
Boa Prtica
Uma boa conduta na definio de variveis do tipo ponteiro colocar um p no incio do nome da varivel para se ter uma distino clara quando se l um programa de Quais variveis so ponteiros e quais no so.
A situao da associao de um ponteiro e um alvo indica se um dado ponteiro est associado a um dado alvo. possvel trs situaes: associado (associated), indefinido (undefined) e desassociado (disassociated). Quando um ponteiro criado em uma declarao de tipo sua situao indefinido, quando associado a um alvo a situao ento associado, e quando desassociado do alvo sua situao torna-se desassociado. J vimos como declarar um ponteiro e como associar um ponteiro a um alvo, resta vermos como desassociar um ponteiro de seu alvo. Para tal necessrio apenas executar uma declarao NULLIFY com o seguinte formato
nullify(lista_de_ponteiros)
115
que responder se ponteiro j est associado ou no. A Segunda forma de uso desta funo com dois argumentos
variavel_logica = associated(ponteiro, alvo)
Quando um ponteiro aparece em uma expresso aritmtica, no lugar de um valor numrico ento o valor da varivel para a qual ele est apontando usado. Quando se escreve
p_x = p_y
o valor que est na varivel alvo apontada por p_y atribuda posio de memria da varivel alvo apontada por p_x. Adicionalmente, se escrevemos
p_x => p_y
significa que estamos apontando o ponteiro p_x para o ponteiro p_y, assim p_x ficar apontando para a mesma posio de memria que p_y, ou seja a varivel alvo de p_y. Abaixo apresenta-se um programa que utiliza ponteiros para trocar a posio de duas matrizes
program usando_pointer !Objetivo: Mostrar o uso de ponteiros ! !programador: joo batista aparecido ilha solteira, 16 de dezembro, 1999 ! implicit none integer, parameter :: i_max = 100, j_max =100 integer :: i,j real :: aux real, target, dimension(i_max,i_max) :: x,y !Definindo os alvos
116
!Fazendo o swap das matrizes da forma clssica do i = 1, i_max do j = 1, j_max aux = x(i,j) x(i,j) = y(i,j) y(i,j) = aux end do end do !Fazendo o swap das matrizes usando ponteiros p_aux => p_x p_x => p_y p_y => p_aux stop end program usando_pointer
Um dos aspectos mais poderosos do uso de ponteiros em alocao dinmica que ele permite no s a criao de espaos na memria como tambm depois de criados aumentar ou diminuir estes espaos sem destruir os dados j armazenados. A maneira de se fazer isto parecida com aquela j vista usando arrays alocveis. Espaos em memria so alocados usando a declarao ALLOCATE, e desalocados usando DEALLOCATE. Neste a declarao ALLOCATE fica da seguinte forma
allocate(ponteiro_1(forma_do_ponteiro_1),,stat = status)
onde ponteiro_1 o nome do primeiro ponteiro a ser alocado, e assim por diante, forma_do_ponteiro_1 a forma, ou seja o rank e a extenso em cada direo que possui a estrutura para a qual o ponteiro vai apontar, e status o resultado para operao, se a alocao for bem sucedida seu valor zero, caso contrrio seu valor ser dependente de zero e depender da implementao de cada compilador, sendo necessrio consultar o manual do fabricante. A operao descrita acima criar um ponteiro que apontar para um conjunto de dados sem nome. Devido ao fato deste objeto no ter nome ele poder ser acessado apenas via ponteiro. Depois que a rotina acima executada com sucesso a situao do ponteiro ser associado. Se o ponteiro (ou ponteiros), que est associado a este objeto sem nome, for nulificado ou redirecionado para outro objeto, aquele objeto estar perdido para sempre. Porm ainda ocupando as posies de memria, criando o
117
Quando um espao de memria desalocado usando DEALLOCATE automaticamente o ponteiro que apontava para aquele objeto nulificado. Logo abaixo apresenta-se a verso do programa anterior, agora usando alocao dinmica de memria
program usando_pointer !Objetivo: Mostrar alocao dinmica usando ponteiros ! !programador: joo batista aparecido ilha solteira, 16 de dezembro, 1999 ! implicit none integer, parameter :: i_max = 100, j_max =100 integer :: status real, pointer, dimension(:,:) :: p_x, p_y, p_aux !Definindo os ponteiros nullify(p_x,p_y,p_aux) !Nulificando os ponteiros allocate(p_x(i_max,j_max),p_y(i_max,j_max),stat = status) !Alocando memria p_x = 1.0 p_y = 5.0 !Inicializando o array para onde aponta p_x !Inicializando o array para onde aponta p_y
!Fazendo o swap das matrizes usando ponteiros p_aux => p_x p_x => p_y p_y => p_aux deallocate(p_x, stat=status) !Desalocando memria deallocate(p_y, stat=status) !Desalocando memria deallocate(p_aux, stat=status)!Desalocando memria stop end program usando_pointer
Pode-se colocar ponteiros dentro de estruturas derivadas de dados. Assim seja a seguinte estrutura derivada de dados
type :: real_value real :: value type(real_value), pointer :: p end type
118
! Description of change ! ===================== ! original code IMPLICIT NONE ! Derived data type to store real values in TYPE :: real_value REAL :: value TYPE (real_value), POINTER :: p END TYPE ! List of variables: TYPE (real_value), POINTER :: head ! Pointer to head of list CHARACTER(len=20) :: filename ! Input data file name INTEGER :: nvals = 0 ! Number of data read TYPE (real_value), POINTER :: ptr ! Temporary pointer TYPE (real_value), POINTER :: tail ! Pointer to tail of list INTEGER :: istat ! status: 0 for success REAL :: temp, aux ! Temporary variable ! Get the name of the file containing the input data. WRITE (*,*) 'Enter the file name with the data to be read: ' READ (*,'(A20)') filename ! open input data file. OPEN ( UNIT=9, FILE=filename, STATUS='OLD', ACTION='READ',IOSTAT = istat )
119
120
A estrutura derivada de dados utilizada acima pode ser estendida para conter no seu interior vrios valores reais, inteiros, lgicos, caracteres, complexos, assim uma extenso nesta direo seria, por exemplo, o que segue
type :: various_values complex :: c1 integer :: int1, int2 real :: value1, value2, value3 character(20) :: nome1, nome2 logical :: logic1, logic2, logic3 type(various_values), pointer :: p end type
Um outro tipo de extenso, na estrutura de dados acima, incorporar mais de uma ponteiro. Um exemplo bem conhecido na literatura a arvore binria que acrescenta mais um ponteiro ao tipo derivado de dados. Assim cada elemento na estrutura global estar apontando para nenhum, um ou dois outros elementos, formando assim uma arvore binria. Uma estrutura razoavelmente genrica de uma rvore binria seria
type :: various_values_and_two_pointers complex :: c1 integer :: int1, int2 real :: value1, value2, value3 character(20) :: nome1, nome2 logical :: logic1, logic2, logic3 type(various_values_and_two_pointers), pointer :: p1, p2 end type
Imagine-se uma arvore binria para armazenar e disponibilizar os primeiro, segundo e terceiro nomes de uma pessoa bem como o seu respectivo telefone. A seguir o programa que usa esta estrutura de dados
MODULE btree ! ! Purpose: ! To define the derived data type used as a node in the ! binary tree, and to define the operations >, <. and == ! for this data type. This module also contains the ! subroutines to add a node to the tree, write out the ! values in the tree, and find a value in the tree. ! ! Record of revisions: ! Date Programmer Description of change ! ==== ========== ===================== ! 02/04/96 S. J. Chapman Original code ! IMPLICIT NONE ! Restrict access to module contents. PRIVATE
121
122
123
124
125
! The file was opened successfully, allocate space for each ! node, read the data into that node, and insert it into the ! binary tree. input: DO ALLOCATE (temp,STAT=istat) ! Allocate node NULLIFY ( temp%before, temp%after) ! Nullify pointers READ (9, 100, IOSTAT=istat) temp%last, temp%first, & temp%mi, temp%phone ! Read data 100 FORMAT (A10,1X,A10,1X,A1,1X,A16) IF ( istat /= 0 ) EXIT input ! Exit on end of data CALL add_node(root, temp) ! Add to binary tree END DO input ! Now, write out the sorted data. WRITE (*,'(/,1X,A)') 'The sorted data list is: ' CALL write_node(root)
126
127
William H. Press, Saul A. Teukolsky, William T. Vetterling and Brian P. Flannery (1996), Numerical Recipes in Fortran 90 (The Art of Parallel Scientific Computing), Vol.2, Cambridge University Press, New York, 562pages Microsoft Corporation (1995), Fortran PowerStation Reference (Version 4.0), Microsoft Corporation, 614 pages. Microsoft Corporation (1995), Fortran PowerStation Programmers Guide (Version 4.0), Microsoft Corporation, 752 pages.
128