sinta´tica e´ caracterizada pela palavra var antes do nome do identifica- dor do paraˆmetro.2 Pode parecer sutil, mas com uma semaˆntica totalmente diferente daquela da passagem de paraˆmetros por valor. O que antes era uma co´pia, agora e´ uma refereˆncia ao enderec¸o da varia´vel do programa principal. Isto e´, no momento da ativac¸a˜o da func¸a˜o, o identificador n da func¸a˜o e´ associado com o mesmo enderec¸o da varia´vel a no programa principal. Consequentemente, qualquer alterac¸a˜o associada a n na func¸a˜o provocara´ alterac¸a˜o do valor da varia´vel a no programa principal. Vejamos na figura 9.4 como fica a modificac¸a˜o no programa em estudo do ponto de vista meramente sinta´tico. Deixamos o ponto de vista semaˆntico para ser explicado no pro´ximo exemplo. 2Com todo o respeito ao Niklaus Wirth, usar a palavra var para este fim na˜o foi uma boa escolha, pois para um principiante e´ fa´cil confundir com uma passagem de paraˆmetros por refereˆncia com uma declarac¸a˜o de uma varia´vel, o que na˜o e´ definitivamente o caso aqui. 114 CAPI´TULO 9. FUNC¸O˜ES E PROCEDIMENTOS program imprime pares ; var a: integer ; (∗ funcao que calcula se a variavel global a eh par ∗) function eh par (var n: integer) : boolean; begin if n mod 2 = 0 then eh par:= true else eh par:= false ; end; begin (∗ programa principal ∗) read (a) ; while a <> 0 do begin if eh par (a) then writeln (a) ; read (a) ; end; end. Figura 9.4: Versa˜o com paraˆmetros por refereˆncia. 9.2.7 Procedimentos Um procedimento difere de uma func¸a˜o basicamente pois na˜o tem um valor de retorno associado. Isto faz com que ele se comporte como um comando extra da linguagem ao passo que a func¸a˜o tem um comportamento mais parecido com o de uma expressa˜o aritme´tica ou booleana. Um proto´tipo para um procedimento e´ definido enta˜o com base em apenas duas informac¸o˜es: 1. o identificador do procedimento (nome); 2. a lista de paraˆmetros (que podem ser por valor ou refereˆncia). Para explicar corretamente a noc¸a˜o de procedimentos, e tambe´m de varia´veis lo- cais, e´ importante mudar de exemplo. Vamos considerar o problema de se ler dois nu´meros reais x e y do teclado, fazer a troca dos conteu´dos e depois imprimir. Vamos considerar o seguinte proto´tipo para um procedimento que recebe os dois valores x e y e tem a finalidade de realizar a troca dos conteu´dos destas varia´veis: procedure troca (var a, b: real) ; Observe que, assim como no caso da func¸a˜o, na˜o importa muito o nome dos identi- ficadores dos paraˆmetros, apenas importa que eles definam conteu´dos do tipo REAL. Podemos enta˜o tentar escrever um programa que leia dois valores e os imprima trocados, conforme ilustrado na figura 9.53: 3Um exerc´ıcio interessante e´ passar os paraˆmetros por valor e compreender o efeito disso. 9.2. NOC¸O˜ES FUNDAMENTAIS 115 program imprimetrocado ; var x,y,temp: real ; (∗ variaveis globais ∗) (∗ procedimento que troca os conteudos da variaveis ∗) procedure troca (var a, b: real) ; begin temp:= a; a:= b; b:= temp; end; begin (∗ programa principal ∗) read (x,y) ; troca (x,y) ; writeln (x,y) ; end. Figura 9.5: Versa˜o com paraˆmetros por refereˆncia. Este programa usa uma varia´vel global (temp) para auxiliar a troca, o que na˜o faz muito sentido. Os subprogramas devem funcionar independentemente de varia´veis globais. 9.2.8 Varia´veis locais Varia´veis locais sa˜o declaradas nos subprogramas e teˆm escopo local, isto e´, elas so´ sa˜o conhecidas durante a execuc¸a˜o do subprograma. Consequentemente na˜o interferem no programa principal. O programa modificado da figura 9.6 faz uso da varia´vel local temp e torna o co´digo mais robusto. program imprimetrocado ; var x,y: real ; (∗ variaveis globais ∗) (∗ procedimento que troca os conteudos da variaveis ∗) procedure troca (var a, b: real) ; var temp: real ; (∗ variavel local , temporaria para uso exclusivo neste procedimento ∗) begin temp:= a; a:= b; b:= temp; end; begin (∗ programa principal ∗) read (x,y) ; troca (x,y) ; writeln (x,y) ; end. Figura 9.6: Versa˜o com uma varia´vel local. 116 CAPI´TULO 9. FUNC¸O˜ES E PROCEDIMENTOS 9.3 Alguns exemplos Nesta sec¸a˜o vamos implementar dois problemas simples para exercitar. 9.3.1 Calculando d´ıgito verificador Vamos fazer um programa que recebe um nu´mero de N d´ıgitos, sendo o u´ltimo deles o “d´ıgito verificador” do nu´mero formado pelos N − 1 primeiros. Devemos calcular se o d´ıgito verificador fornecido pelo usua´rio esta´ correto segundo o esquema de ca´lculo seguinte: cada d´ıgito do nu´mero, comec¸ando da direita para a esquerda (menos sig- nificativo para o mais significativo) e´ multiplicado, na ordem, por 1, depois 2, depois 1, depois 2 e assim sucessivamente. O nu´mero de entrada do exemplo e´ 261533-4. +---+---+---+---+---+---+ +---+ | 2 | 6 | 1 | 5 | 3 | 3 | - | 4 | +---+---+---+---+---+---+ +---+ | | | | | | x2 x1 x2 x1 x2 x1 | | | | | | =4 =6 =2 =5 =6 =3 +---+---+---+---+---+-> = 26 Como 26 tem dois d´ıgitos, vamos repetir o processo acima ate´ gerarmos um nu´mero de um u´nico d´ıgito. Assim: +---+---+ | 2 | 6 | +---+---+ | | x2 x1 | | =4 =6 +---+ = 10 Como 10 ainda tem dois d´ıgitos, o algoritmo roda ainda mais uma vez: +---+---+ | 1 | 0 | +---+---+ | | x2 x1 | | =2 =0 +---+ = 2 Assim, o d´ıgito verificador calculado (2) difere daquele fornecido (4) e o programa deveria acusar o erro. O programa da figura 9.7 ilustra uma poss´ıvel soluc¸a˜o. 9.3. ALGUNS EXEMPLOS 117 program digitoverificador ; var numero, n: longint ; dv, dv correto : integer ; procedure le (var n: longint) ; begin {$i−} repeat read (n) ; until ioresult = 0; {$i+} end; procedure separa numero e dv (n: longint ; var num: longint ; var dv: integer) ; begin num:= n div 10; dv:= n mod 10; end; function calcula dv correto (n: longint) : integer ; var soma, mult, ultimo : integer ; begin repeat soma:= 0; mult:= 1; while n <> 0 do begin ultimo:= n mod 10; n:= n div 10; soma:= soma + mult ∗ ultimo ; i f mult = 1 then mult:= 2 else mult:= 1; end; n:= soma; until (n > 0) and (n<= 9) ; calcula dv correto:= soma; end; begin (∗ programa principal ∗) le (numero) ; separa numero e dv (numero, n, dv) ; dv correto:= calcula dv correto (n) ; i f dv correto <> dv then writeln (’digito verificador invalido.’) end. Figura 9.7: Calculando d´ıgito verificador. 118 CAPI´TULO 9. FUNC¸O˜ES E PROCEDIMENTOS O importante para se observar neste co´digo e´ a clareza do algoritmo no programa principal. O leitor pode acompanhar este trecho e perceber claramente as diversas etapas em uma linguagem de bastante alto n´ıvel: leitura do nu´mero, separac¸a˜o deste em duas partes, uma contendo os primeiros d´ıgitos a outra contendo o dv de entrada. Em seguida o ca´lculo do d´ıgito verificador correto e finalmente a comparac¸a˜o dos dados calculados com o de entrada, gerando a mensagem final. No programa principal pode-se ignorar completamente como sa˜o feitas todas as operac¸o˜es nas func¸o˜es e procedumentos: na˜o importa como os dados sa˜o lidos, nem como os d´ıgitos sa˜o separados, e muito menos como e´ feito o ca´lculo do d´ıgito verifi- cador correto. No programa principal o importante e´ o algoritmo em alto n´ıvel. E´ claro que em algum momento sera´ necessa´rio escrever co´digo para cada uma das func¸o˜es e procedimentos, mas quando isto for feito o programador estara´ resolvendo um subproblema de cada vez, o que facilita muito a construc¸a˜o do co´digo para o problema global. Por exemplo, a leitura poderia ser feita em uma interface gra´fica ou textual. Foi es- colhida nesta versa˜o uma interface textual, mas que permite testes de consisteˆncia dos dados de entrada. Isto, feito separadamente, mante´m