Professional Documents
Culture Documents
Compilao de artigos
relacionados com a
linguagem de programao
Object Pascal
Revista PROGRAMAR
Revista PROGRAMAR
ndice
33 Edio
Programao Orientada aos Objectos em Pascal
34 Edio
Mdulos em Pascal
19
35 Edio
Pascal Registos variantes
29
36 Edio
Pascal Construo de uma calculadora grfica
38
42 Edio
Pascal Tipos de dados Variant e tipos genricos
70
44 Edio
Pascal operator overloading
78
45 Edio
Pascal array de argumentos
88
Revista PROGRAMAR
33 Edio
Revista PROGRAMAR
Introduo
H quem pense no Pascal como uma linguagem fraca e antiquada que no
acompanhou os novos tempos. Mas, na realidade, o Pascal evoluiu em vrios dialectos,
sendo o mais proeminente o Delphi.
Hoje em dia possvel programar
nesta
linguagem
segundo
o
paradigma
POO
(Programao
Orientada aos Objectos), programar
DLLs (Dynamic Link Libraries), fazer
programas no s em consola mas
tambm com recurso a GUI. Permite
igualmente a ligao a bases de
dados, como o MySQL, bem como a
criao de IDEs. O grande exemplo
o, infelizmente descontinuado, IDE
Dev-Pascal, da Bloodshed, um IDE
open-source, escrito totalmente em
Delphi e recorrendo a uma velha
verso do FPC (Free Pascal Compiler) e do GPC (GNU Pascal Compiler) como
compiladores opcionais integrados.
o Pascal
evoluiu em
vrios dialectos,
sendo o mais
proeminente o
Delphi.
Revista PROGRAMAR
Preliminares em POO
O mbito do presente no artigo no uma explicao
alongada deste paradigma. Contudo, vale uma explicao
no mbito do Pascal e daquilo que ser utilizado adiante.
Sero ento abordados os conceitos principais deste
paradigma, sendo estes: classes, mtodos, propriedades,
instncias, herana e visibilidade (ou proteco).
Igor Nunes uma instncia desta classe, por exemplo. Sendo uma instncia,
Igor Nunes ter exactamente as propriedades altura, peso, idade, etc., bem como os
mtodos andar, falar, dormir, etc. J a instncia Joo ter exactamente as mesmas
propriedades e mtodos. O prprio leitor uma instncia desta classe! Contudo, de
ter em ateno que cada instncia tem valores distintos para cada propriedade altura,
peso, idade
Revista PROGRAMAR
Herana
Uma das capacidades mais impressionantes e teis do paradigma POO a
possibilidade de uma classe herdar mtodos e propriedades de outra. Neste mbito,
temos ento duas classes, uma que deriva de outra:
Classe principal (ou Main Class) classe que tem os mtodos e
propriedades gerais;
Classe derivada (ou Inherited Class) classe que herda os
mtodos e propriedades da anterior deriva da classe principal.
Voltando classe Ser Humano:
1. Mamfero uma classe principal de Ser Humano;
2. Caucasiano uma classe derivada de Ser Humano;
3. Reino Animal uma classe principal de Mamfero;
Representao de herana
entre classes em UML.
Revista PROGRAMAR
O Object Pascal
esta
linguagem
tornou-se muito
mais poderosa e
moderna
Classes
Um conjunto novo de palavras reservadas surgiu, e entre elas esto as palavras
reservadas Object e Class. de referir que um Object pode ser qualquer coisa, sendo,
portanto, a me de todos os tipos de dados, incluindo procedimentos e funes.
Para agora, a palavra que nos interessa a Class. Com esta palavra reservada
criamos uma classe, tal como o seu nome sugere.
Em Pascal, uma classe dever ser sempre declarada num tipo (type) j que as
instncias da classe sero criadas como variveis (var).
Revista PROGRAMAR
Este tipo no comea com a palavra reservada begin mas sim com a prpria
palavra class, mas termina obrigatoriamente com end. Dentro deste bloco que se cria
teremos a declarao, mas no programao, dos mtodos da classe. Igualmente, aqui
estaro as propriedades.
Para definir as visibilidades, podemos criar trs blocos. Nenhum deles necessita
de estar num bloco begin-end j que se considera que o bloco termina quando
encontrada a declarao de um outro bloco, ou o fim da classe.
Na prtica, a estrutura de uma classe ser a seguinte:
type Nome_Classe = class
private
// parte privada
protected
// parte protegida
public
// parte pblica
end;
Mtodos
A parte principal de uma classe o conjunto dos seus mtodos as
habilidades da classe, os processos que esta capaz de realizar1. E, em Pascal, os
mtodos no so mais do que procedimentos e funes.
Na classe, estes apenas so declarados. Nunca so implementados nesta parte:
type Nome_Classe = class
private
function funcao(const texto : tipo_dado) : tipo_output;
public
procedure procedimento(const argumentos : tipo_dado);
end;
Neste caso no temos mtodos protegidos. Como podemos verificar, os mtodos
da classe so declarados dentro dos seus blocos.
Ento, onde so implementados? A resposta : tal como outro procedimento e
funo quaisquer: fora do bloco principal do programa.
Pode-se dizer que os mtodos so a parte principal j que, regra geral, uma classe no tem sentido em
existir s para ter atributos/propriedades. Excepo feita, por exemplo, a classes criadas para o efeito em
que se tornam extremamente teis para organizar informao com uma estrutura semelhante ou igual.
Revista PROGRAMAR
10
Revista PROGRAMAR
Propriedades
Uma propriedade, por si s, no possui nenhum valor. Uma propriedade um
atalho para uma varivel que, e essa sim, possui um valor especfico. Por exemplo, o
nosso peso uma medida de massa que aparece na balana, mas, por detrs desse
valor, est um mecanismo que mediu, registou e fez output desse mesmo valor. Neste
exemplo, abordmos tudo o que h numa propriedade:
Input de um dado (com o seu devido registo);
Output desse mesmo dado (com a sua devida leitura).
Uma propriedade , ento, um atributo da classe que inclui, opcionalmente, um
conjunto de dois mtodos (um input e outro output) que atribuem e lem o valor de uma
varivel da classe. Opcionalmente por uma razo: uma propriedade ser ReadOnly (s
de leitura) se s fornecer output e WriteOnly (s de escrita) se s poder ser atribuda
mas no acedida para leitura.
Considerando o caso geral de uma propriedade que permite leitura e escrita:
property propriedade : tipo_dado read metodo_leitura write metodo_escr
ita;
Para melhor entender este conceito, vamos partir de um exemplo prtico e que
ser j parte da nossa implementao prtica:
type TAngle = class
private
VDEG : real; // Varivel da qual depende a propriedade
procedure DefinirValorDEG(const valor : real);
public
property ValorDEG : real read VDEG write DefinirValorDEG;
end;
procedure TAngle.DefinirValorDEG(const valor : real);
(* Atribui valor propriedade em graus *)
begin
self.VDEG := valor;
end;
Isto o que acontece na prtica e na grande maioria das propriedades. Como
podemos ver, a propriedade, de seu nome ValorDEG, tem uma varivel por detrs
que possui o seu valor: a varivel privada VDEG.
Aqui comeamos j a ganhar noo da vantagem das visibilidades das
propriedades: o que nos interessa que se veja a propriedade da classe, e no a
varivel na qual est atribuda o valor dessa propriedade. Assim, mantemos essa
varivel privada.
11
Revista PROGRAMAR
Para fazer o output da propriedade, bastar ler o valor da sua varivel VDEG,
pelo que o nome do mtodo de escrita o prprio nome da varivel. O mtodo de
escrita dever ser sempre uma funo, mas como a varivel em si uma funo, este
processo torna-se vlido.
J a escrita necessita de um procedimento. Neste caso, crimos o
procedimento DefinirValorDEG que tem nica e exclusivamente um argumento. Todo
e qualquer mtodo de escrita dever ter um e um s argumento, sendo este do mesmo
tipo que a varivel da qual depende a propriedade. este argumento que recebe o valor
a ser atribudo propriedade, leia-se varivel.
Neste caso, DefinirValorDEG recebe valor e atribu-o varivel VDEG. Para
no confundir com nenhuma varivel global com o mesmo nome que pode ser criado
para o programa, utilizamos a palavra reservada self, que indica ao compilador que a
varivel VDEG a referente classe cujo mtodo est ali a ser programado: neste caso,
a classe TAngle.
Construtores
Uma instncia de uma classe, que ser vista a seguir, no pode surgir do nada.
A instncia tem de ter um construtor de modo a que a varivel que criarmos seja, de
facto, uma instncia. Caso no faamos a construo, a varivel do tipo da classe ser
uma referncia nula e, logo, surgir uma excepo na execuo do programa.
O tipo de dados geral TObject tem o seu
construtor: o Create. Vamos invocar este
construtor nas nossas classes para podermos
construir o nosso. O processo de herana ser
feito pela palavra reservada inherited.
O construtor dever ser pblico, e
declarado pela palavra reservada constructor.
Poder receber argumentos ou no, depende do
processo de construo da classe.
Regra geral, com o construtor damos os
valores por default s propriedades da classe. Estes valores podero vir dos
argumentos.
type Nome_Classe = class
private
// parte privada
protected
// parte protegida
public
constructor Create({argumentos do construtor});
end;
12
Revista PROGRAMAR
Instncia
Por fim, agora que a classe est criada, vamos criar instncias desta. Uma
instncia criada por uma varivel do tipo da classe. Em Pascal:
var Instncia : Nome_Classe;
No bloco de execuo, antes de utilizar qualquer mtodo ou atribuir ou ler
qualquer propriedade, necessrio criar a instncia atravs do construtor:
Instncia := Nome_Classe.Create({argumentos do construtor});
Note-se bem que, no construtor, o nome da classe que se coloca e no o nome
da instncia!
E, daqui em diante, pode-se utilizar qualquer mtodo da classe, atribuir e ler
propriedades desta, mas, para cada instncia, cada valor. Se tivermos dez instncias,
cada uma pode ter um valor diferente para uma mesma propriedade. Isto perceptvel
no caso da classe Ser Humano:
O Joo pesa 75Kg;
O Andr pesa 64Kg;
A Joana pesa os seus leves 52Kg;
J a Mariana chega aos 86Kg;
Destrutor
J que neste artigo no vamos criar destrutores, fica a referncia que, para
qualquer classe, podemos utilizar o destrutor geral: o Free.
Antes de terminar um programa, as instncias devem ser, ento, destrudas:
Instncia.Free;
13
Revista PROGRAMAR
Implementao prtica
Terminada que est a introduo terica utilizao de Programao Orientada
aos Objectos em Pascal, a melhor forma de consolidar estes conhecimentos com um
exerccio prtico.
Criemos, ento, uma classe, de seu nome TAngle, e que permita guardar o valor
de um ngulo em graus e radianos e tenha as devidas funes de converso. Para tal,
teremos:
Mtodos:
o Conversor radianos graus;
o Conversor graus radianos.
Propriedades:
o Valor do ngulo em graus;
o Valor do ngulo em radianos.
14
Revista PROGRAMAR
15
Revista PROGRAMAR
end;
function TAngle.Deg2Rad(const deg : real) : real;
begin
result := (pi * deg) / 180;
end;
function TAngle.Rad2Deg(const rad : real) : real;
begin
result := (rad * 180) / pi;
end;
16
Revista PROGRAMAR
Concluso
Aps este artigo, percebemos que o Pascal, j h
quase 30 anos atrs, suporta o paradigma da Programao
Orientada aos Objectos sem que a sua sintaxe original
tivesse de ser alterada: apenas houve ligeiras adaptaes e
a entrada de novas palavras reservadas.
A entrada deste paradigma reforou ainda mais o
objectivo desta linguagem de programao: a programao
estruturada, com cabea, tronco e membros como se diz na
gria popular.
Conclui-se, sendo assim, que, apesar de muitos lhe
chamarem uma linguagem ultrapassada, o Pascal est longe
de ser uma linguagem do passado. Pascal uma linguagem
do sculo XXI.
o Pascal
est longe de ser
uma linguagem
do passado.
Pascal uma
linguagem do
sculo XXI.
Links teis
Uma lista de documentos teis da Wiki P@P, relacionados com o presente
artigo.
17
Revista PROGRAMAR
Sobre o autor
Igor Nunes
Entrou no mundo da programao aos 14 anos com TI-Basic. Dois anos depois
descobriu o Pascal, que ainda hoje a sua Linguagem de Programao de eleio.
Mais recentemente introduziu-se ao VB.NET e ao Delphi.
Membro do P@P desde Abril de 2010 (@thoga31), actualmente membro da
Wiki Team e Moderador Local dos quadros de Pascal e Delphi/Lazarus. Escreveu o
novo Tutorial de Pascal da Wiki P@P, bem como os Tutoriais de TI-Basiz Z80 e de
Introduo Lgica e Algoritmia.
18
Revista PROGRAMAR
34 Edio
Pascal Mdulos
Por: Igor Nunes
19
Revista PROGRAMAR
20
Revista PROGRAMAR
Estrutura de um Mdulo
Um Mdulo um conjunto de cdigo extremamente bem estruturado, iniciado
com a palavra reservada unit, estando dividido em blocos variados, desde blocos de
declarao das funes e procedimentos at de finalizao. Estes blocos, com
respectivas palavras reservadas que as identificam no Mdulo, so os seguintes:
Interface e Implementao
Um mdulo, sendo um conjunto de cdigo a ser implementado num qualquer
programa que o importe, dever ter uma zona que identifique que funes e
procedimentos tem, e s depois haver uma zona de implementao.
A forma como as funes so declaradas na Interface dever coincidir
perfeitamente com a implementao feita. Isto torna-se claro com um exemplo.
{$MODE Delphi}
UNIT modulo_exemplo;
//declarao do mdulo
INTERFACE (*Interface - declarao*)
function Positivo
(const numero:integer):boolean;
21
Revista PROGRAMAR
IMPLEMENTATION (*Implementao*)
(* 1
Errado *)
function Positivo;
(* 2
Errado *)
function Positivo
(numero:integer):boolean;
(* 3
Errado *)
function Positivo
(const num:integer ):boolean;
(* 4 Correcto *)
function Positivo
(const numero:integer):boolean;
begin
If (numero > 0) then
result:=true
else
result:=false;
end;
END.
22
Revista PROGRAMAR
Inicializao e Finalizao
Sendo os blocos de interface e de implementao obrigatrios, os blocos agora
abordados so totalmente opcionais os de inicializao e os de finalizao.
Como referido anteriormente, o bloco de inicializao permite-nos realizar certas
operaes no incio do programa. Estas operaes so realizadas antes de qualquer
processo do programa onde o mdulo est includo. J as operaes localizadas no
bloco de finalizao so executadas imediatamente antes do programa fechar.
Para quem conhece bem Visual Basic .NET, fica uma ptima comparao:
quando a Form abre, todos os processos do evento Load so executados equivale ao
bloco de inicializao do nosso mdulo em Pascal. Quando pretendemos que outros
processos sejam feitos mesmo antes da Form fechar, colocamo-los no evento
FormClosing (e no no FormClosed) equivale ao nosso bloco de finalizao.
Vamos recorrer a duas Message Boxes para mostrar a aco destes blocos num
programa que inclua o mdulo que se segue, e cuja explicao se encontra de seguida.
UNIT Modulos_Pascal;
INTERFACE
uses windows;
procedure Escrever(texto:string);
IMPLEMENTATION
procedure Escrever(texto:string);
var conv:string;
i:word;
begin
conv:='';
texto:=UpCase(texto);
for i:=1 to length(texto) do
begin
if not(texto[i]='Z')
then conv:=conv+succ(texto[i])
else conv:='A';
end;
WriteLn(conv);
end;
INITIALIZATION
MessageBox(0,
'A unit "Modulos_Pascal" est neste programa!',
'Mdulos Pascal', MB_ICONASTERISK);
FINALIZATION
MessageBox(0, 'At prxima!' ,
'Mdulos Pascal', MB_ICONASTERISK);
END.
23
Revista PROGRAMAR
24
Revista PROGRAMAR
25
Revista PROGRAMAR
UNIT Registos;
INTERFACE
TYPE TRegPessoa = RECORD
Nome:string[80];
Idade:byte;
BI:string[9];
Profissao:string[40];
END;
//resto do mdulo
A partir deste momento, este tipo de dado pode ser utilizado no s no mdulo
como tambm no programa que o incluir:
PROGRAM Teste;
USES Registos;
VAR Pessoas :
array[1..100] of TRegPessoa
//resto do programa
modulo~1.ppw
modulo~1.ow
modulos_pascal.ow
modulos_pascal.ppw
26
Revista PROGRAMAR
Ateno! necessrio ter em conta que isto pode variar conforme o compilador
utilizado. Se recorrer a um compilador que no o Free Pascal, verifique os ficheiros
gerados pela compilao do mdulo. Se forem diferentes, faa experincias ou ento
copie todos os ficheiros gerados. No far muita diferena no que toca a espao em
disco j que estes ocupam uma mdia de 2 a 10 KB cada um, conforme a quantidade
de cdigo do mdulo.
Concluso
Com este artigo ficmos a conhecer uma das maiores potencialidades do Pascal:
a hiptese de se criar mdulos prprios e que podem ser utilizados por qualquer
programa que necessite dos processos neles implementados.
Descobrimos assim o que est por detrs da palavra reservada uses. Um
mdulo recheado de procedimentos e funes teis, bem como novos tipos de dados.
Os mdulos so um dos exemplos mximos de boas prticas de programao
em Pascal. Para um programa complexo torna-se muito til organizar os vrios
processos em mdulos diferenciados, sendo que cada um ter processos relacionados.
Por exemplo, processos matemticos ficam num mdulo e processos de manipulao
de Strings noutra. No fim inclui-se estes mdulos no programa final na palavra reservada
uses e o programa ter um cdigo muito mais limpo j que o cerne est nos mdulos.
Links teis
Uma lista de documentos teis da Wiki P@P, relacionados com o presente
artigo.
27
Revista PROGRAMAR
Sobre o autor
Igor Nunes
Estudante universitrio, entrou no mundo da programao aos 14 anos com TIBasic. Dois anos depois descobriu o Pascal, que ainda hoje a sua Linguagem de
Programao de eleio. Mais recentemente introduziu-se ao VB.NET e ao Delphi.
Membro do P@P desde Abril de 2010 (@thoga31), actualmente membro da
Wiki Team e Moderador Local dos quadros de Pascal e Delphi/Lazarus. Escreveu o
novo Tutorial de Pascal da Wiki P@P, bem como os Tutoriais de TI-Basiz Z80 e de
Introduo Lgica e Algoritmia.
28
Revista PROGRAMAR
35 Edio
29
Revista PROGRAMAR
O campo Nome pode ser acedido fazendo Formulario.Nome, ou seja, referese qual o registo a que queremos aceder e com um ponto separamos o nome do registo
do seu respectivo campo que pretendemos manipular. Isto em Pascal ser o seguinte:
30
Revista PROGRAMAR
TYPE
(* Estado Civil *)
TEstCiv =
(Solteiro,Casado,Outro);
(* Registo *)
TFormulario=RECORD
Nome:String;
Idade:Byte;
Sexo:Char;
BI:String[8];
EstadoCivil:TEstCiv;
END;
VAR
Pessoa:TFormulario;
(* Manipular "Nome" *)
Pessoa.Nome:='Jose';
Isto simplifica muito, pois evita escrevermos sempre o nome do registo. Ou seja,
este bloco infra-apresentado substitui o seguinte:
Pessoa.Nome:='Jose';
Pessoa.Idade:=63;
Pessoa.Sexo:='M';
Pessoa.BI:='12345678';
Pessoa.EstadoCivil:=Casado;
Os registos variantes
31
Revista PROGRAMAR
A questo que se coloca : vale a pena trs registos distintos para cada um dos
produtos? A resposta : no. Num s registo vamos variar os campos conforme o tipo
de produto. Como?
Um determinado campo esttico determinar quais os campos variantes que iro
surgir. Por exemplo, ao indicarmos a um campo esttico, destinado para o efeito, que
estamos a registar uam impressora, surgir um campo variante que indicar se a
impressora a laser ou a jacto de tinta.
A estrutura que nos vai criar campos variveis a partir de campos estticos ser
a Case Of.
TYPE
TConjuntoProdutos =
(PC, Impressora, Rato);
// ...
case TipoProduto :
TConjuntoProdutos of
PC : ;
Impressora : ;
Rato : ;
end;
32
Revista PROGRAMAR
case TipoProduto :
TConjuntoProdutos of
PC : (CPU:string[30];
HDD:string[50]);
Impressora :
(Tinta:TTipoImpressao);
Rato : (Optico:Boolean);
end;
33
Revista PROGRAMAR
34
Revista PROGRAMAR
Temos aqui dez registos de produtos, registos estes que tm campos variantes.
O que vai acontecer?
Comecemos no ndice 1 da matriz. Estamos a registar um PC, pelo que
TipoProduto ser PC e os campos variantes sero HDD e CPU. Seguindo para o ndice
2 definimos uma impressora, e o campo variante ser Tinta. J no ndice 3 definimos
um rato e o campo variante ser Optico.
35
Revista PROGRAMAR
Constatamos que, para uma mesma matriz, cada ndice tem um conjunto de
campos diferente os campos em comum so os estticos e aqueles que diferem so,
obviamente, os variantes.
Verificamos, portanto, mais um poder dos registos variantes. Uma matriz destes
registos vai diferir nos campos em cada ndice. Estamos habituados a ver uma matriz
de um tipo de dado certo e que, para cada ndice, nada muda (a no ser os valores
atribudos, claro). Com os registos variantes isto no acontece.
A seguinte tabela mostra o exemplo de um excerto dos primeiros cinco ndices
do Array unidimensional RegProduto, declarado anteriormente. A tabela s faz
referncia aos campos variantes e ao campo esttico que os varia.
ndice
do
Array
1
2
3
4
5
Campos variantes
TipoProduto
PC
Impressora
Rato
Rato
Impressora
HDD
CPU
SATA 1TB
i5 2.80GHz
Tinta
Optico
JactoTinta
True
False
JactoTinta
Concluso
Ficmos a conhecer, por fim, mais uma das capacidades poderosas do Pascal.
Para qu criar uma srie de registos semelhantes se lhes podemos variar certos campos
consoante parmetros muito especficos?
Os registos ganham, assim, uma nova dimenso aos nossos olhos. Simplificam
o nosso trabalho em larga escala e demonstram capacidades que no muitas
linguagens tm neste aspecto. E esta uma capacidade que j vem desde os primrdios
da existncia do Pascal, ou seja, desde os anos 70.
Links teis
Uma lista de documentos teis da Wiki P@P, relacionados com o presente
artigo.
36
Revista PROGRAMAR
Sobre o autor
Igor Nunes
Estudante universitrio, entrou no mundo da programao aos 14 anos com TIBasic. Dois anos depois descobriu o Pascal, que ainda hoje a sua Linguagem de
Programao de eleio. Mais recentemente introduziu-se ao VB.NET e ao Delphi.
Membro do P@P desde Abril de 2010 (@thoga31), actualmente membro da
Wiki Team e Moderador Local dos quadros de Pascal e Delphi/Lazarus. Escreveu o
novo Tutorial de Pascal da Wiki P@P, bem como os Tutoriais de TI-Basiz Z80 e de
Introduo Lgica e Algoritmia.
37
Revista PROGRAMAR
36 Edio
38
Revista PROGRAMAR
Muitos acharo que Pascal e as suas variantes, com a excepo para o Delphi,
mais no fazem se no programas para consola, ou seja, Console Applications. Isso
seria verdade no fosse o facto do Turbo Pascal ter dado h vrios anos, ainda na
dcada de 80, um grande passo para a criao de ambientes grficos mais apelativos.
Tinha surgido, ento, a unidade Graph. Esta nova unidade traz consigo uma
panplia de mtodos que nos permitem criar desenhos pixel a pixel, ou seja, criam
ambientes para alm da tradicional e velhinha consola.
Na sua essncia mais simples, esta unidade detecta as caractersticas grficas
do computador onde corre o programa e cria uma rea grfica onde se podem criar os
mais variados desenhos e textos.
Para quem est familiarizado com Visual Basic .NET, a classe Graphics bem
como as namespaces Drawing e Drawing2D so os exemplos com termo de
comparao mais prximo.
Para auxiliar o programador, alguns novos tipos de dados foram includos na
unidade types, sendo de enfatizar as duas mais simples e usuais, TPoint e TSize.
O objectivo deste artigo no ser, contudo, dar noes introdutrias desta
unidade de um ponto de vista puramente terico. Esta unidade to nica e to diferente
de qualquer outra que o Free Pascal oferece que merece uma abordagem totalmente
diferente.
Para dar a conhecer devidamente o poder desta unidade, o objectivo do artigo
ser a construo daquilo que a base de qualquer calculadora grfica: uma Plotter.
Para quem no est familiarizado com o termo, uma Plotter no mais do que
uma calculadora grfica. As suas capacidades so diversas, consoante as
funcionalidades que o seu programador lhe coloque. Contudo, qualquer Plotter ter de
receber uma funo, calcular os seus valores, que sero pontos no grfico, e unir esses
mesmos pontos com linhas de forma a dar uma visualizao contnua e no picotada.
Assim sendo, neste artigo ser criada uma pequena unidade que ter um novo
tipo de dado que conter as propriedades e os mtodos bsicos de uma Plotter uma
classe, portanto , bem como um pequeno programa de teste que ir usar essa unidade
para representar graficamente a seguinte funo:
() = 2 8
Ou seja, esperamos que o resultado final da utilizao da unidade por ns criada
para este feito seja semelhante ao da Figura 1.
39
Revista PROGRAMAR
Logo com esta imagem denotamos as diferenas entre a rea grfica criada pela
unidade Graph e a consola a que estamos to acostumados: a personalizao colorida
pixel a pixel.
Mais uma vez, a par dos meus artigos anteriores, identificarei sempre a
linguagem de programao por Pascal e no por Object Pascal por uma questo de
simplificao. Ser usada, doravante, as notaes unit ou unidade, e no mdulo.
Todos os mtodos e propriedades, tipos de dados e constantes sero nomeados
em ingls.
Por fim, o cdigo aqui presente foi todo feito para ser compilado no Free Pascal
2.6.0 em Windows. Algumas adaptaes podero ter de ser feitas para Mac OS e para
GNU/Linux.
40
Revista PROGRAMAR
Para se ter uma noo geral da imensido desta unit, esta contm:
Mais de 70 mtodos de desenho e personalizao;
Mais de 200 constantes;
Mais de 20 tipos de dados.
Figura 2 A rea grfica criada pelo InitGraph com a tradicional consola sobre ela.
41
Revista PROGRAMAR
42
Revista PROGRAMAR
Rectngulos;
Polgonos;
Elipses;
Crculos;
Queijos (pies);
A unit Graph permite tambm usar preenchimentos que no sejam slidos mas
que tenham no seu lugar um padro (pattern), sendo mesmo possvel criar padres
personalizados, entre muitas outras capacidades.
Dada esta muito breve introduo genrica unidade sobre a qual vamos
trabalhar, chegado o momento de iniciar a construo da Plotter, mas sem antes
definir o que esta far.
43
Revista PROGRAMAR
44
Revista PROGRAMAR
A nossa classe ser, ento, a TPlotter e esta ir herdar daquela que classe
principal do Object Pascal: a TObject.
TPlotter = class(TObject)
Iremos definir os novos tipos de dados quanto a classe for construda, j que,
desta forma, se tornar mais bvia a necessidade no s da sua existncia mas tambm
da sua estrutura final.
Devido s dificuldades que se colocam em explicar a construo de uma classe
destas, iremos construi-la por passos segundo a ordem lgica de construir primeiro os
alicerces e s depois o telhado todos estes passos sero, doravante, numerados para
uma melhor interligao entre estes.
45
Revista PROGRAMAR
46
Revista PROGRAMAR
47
Revista PROGRAMAR
TYPE
TLineStyle = PACKED RECORD
Color : word;
Thickness : word;
Style : word;
Visible : boolean;
END;
A visibilidade fica definida pois tornar-se- fcil controlar quais as linhas que
aparecem fazendo um controlo com uma estrutura de controlo if then.
Desta forma, se definirmos Grid.Visible a False, a grelha no desenhada e o
output j ser do gnero da Figura 5.
48
Revista PROGRAMAR
PRIVATE
vZoom : TZoom;
PUBLIC
property Zoom : TZoom read vZoom;
Ora, temos de definir o tipo de dados TZoom. A Figura 6 representa os dados
que devem estar presentes neste.
Com base nesta definio de Zoom, podemos definir o tipo TZoom do seguinte
modo:
TYPE
TZoom = PACKED RECORD
XMin, XMax, XScl : real;
YMin, YMax, YScl : real;
END;
49
Revista PROGRAMAR
Dimenso
Zoom. Mximo Zoom. Mnimo
Como este clculo interno e ser necessrio para alguns mtodos (bem como
o clculo do centro), vamos declar-lo como um procedimento privado. E como h uma
distncia para cada um dos eixos, vamos aproveitar a estrutura do TPoint para o gravar:
PRIVATE
DistBetweenGrid : TPoint;
procedure CalcDistGridAndCenter;
Implementando este procedimento segundo a frmula infra-apresentada:
PROCEDURE TPlotter.CalcDistGridAndCenter;
begin
// eixo XX:
self.DistBetweenGrid.X :=
Round(self.Zoom.XScl *
(self.Size.cx / (self.Zoom.XMax -
50
Revista PROGRAMAR
self.Zoom.XMin)));
// eixo YY:
self.DistBetweenGrid.Y :=
Round(self.Zoom.YScl *
(self.Size.cy / (self.Zoom.YMax self.Zoom.YMin)));
end;
Falta acrescentar a este procedimento o clculo do centro. O centro depende do
Zoom, e poder no ser necessrio calcul-lo:
Se o Zoom estiver definido tal que a janela esteja toda na rea positiva do eixo
XX (por exemplo, XMin vale 2 e XMax vale 8), ento o centro estar fora da rea
e pode-se definir a coordenada X do centro como sendo a posio X da Plotter.
Tendo em conta esta condio, e sabendo que o centro pode ser calculado
segundo a frmula que se segue, resta-nos implement-lo em Pascal.
Centro = Posio + |Zoom. Mnimo|
Distncia_entre_linha_grelha
Zoom. Escala
// centro em X:
if (self.Zoom.XMin < 0) and
(self.Zoom.XMax > 0) then begin
self.vCenter.X :=
round(self.Position.X +
abs(self.Zoom.XMin) *
(self.DistBetweenGrid.X /
self.Zoom.XScl));
end else begin
if (self.Zoom.XMin < 0) and
(self.Zoom.XMax < 0) then
self.vCenter.X :=
self.Position.X +
self.Size.cx
else self.vCenter.X :=
self.Position.X;
end;
// centro em Y:
if (self.Zoom.YMin < 0) and
(self.Zoom.YMax > 0) then begin
self.vCenter.Y :=
round(self.Position.Y +
abs(self.Zoom.YMax) *
(self.DistBetweenGrid.Y /
self.Zoom.YScl));
51
Revista PROGRAMAR
6. Constantes necessrias
Agora que a base em termos de propriedades e valores necessrios est feita,
e antes de se avanar para a criao dos construtores, podemos definir as constantes
discutidas aquando a definio terica da classe.
Recordando, as constantes sero as seguintes:
CONST
// Zoom:
ZoomStandard : TZoom =
(XMin:-10; XMax:10; XScl:1;
YMin:-10; YMax:10; YScl:1);
// Grelha:
DefaultGrid : TLineStyle =
(Color:DarkGray; Thickness:NormWidth;
Style:DashedLn; Visible:True);
// Eixos:
DefaultAxes : TLineStyle =
(Color:White; Thickness:ThickWidth;
Style:SolidLn; Visible:True);
// Linha de funo:
DefaultFunctionLine : TLineStyle =
(Color:Red; Thickness:ThickWidth;
Style:SolidLn; Visible:True);
// Cor de fundo:
DefaultPlotterBackColor : word = Green;
52
Revista PROGRAMAR
o
o
o
o
XScl = 1
YMin = -10
YMax = 10
YScl = -1
7. Construtores
O meu objectivo no vai ser criar uma panplia de construtores com todos os
overloads possveis. Criarei o construtor completo e, de seguida, criarei uma verso
muito mais simples. A partir destes ser fcil criar mais construtores basta recorrer
directamente ao construtor completo.
O meu construtor completo ir receber 7 parmetros, permitindo uma total
personalizao da instncia da Plotter a priori. O construtor mais simples ir receber
apenas os primeiros 2 parmetros, que sero a posio e a dimenso.
PUBLIC
constructor Create
(cPosition : TPoint; cSize : TSize);
constructor Create
(cPosition : TPoint; cSize : TSize;
cZoom : TZoom;
cGrid, cAxes, cFnLine : TLineStyle;
cBkColor : word); overload;
A implementao do construtor em si simples: simplesmente atribuem-se os
parmetros s respectivas propriedades, e fazem-se os clculos do Passo 5:
53
Revista PROGRAMAR
CONSTRUCTOR TPlotter.Create
(cPosition : TPoint; cSize : TSize;
cZoom : TZoom;
cGrid, cAxes, cFnLine : TLineStyle;
cBkColor : word); OVERLOAD;
begin
self.vPosition := cPosition;
self.vSize := cSize;
self.vZoom := cZoom;
self.Grid := cGrid;
self.Axes := cAxes;
self.FunctionLine := cFnLine;
self.vFunction := nil;
self.BackColor := cBkColor;
self.CalcDistGridAndCenter;
end;
O construtor mais simples ir-se- servir do construtor completo, e ir atribuir aos
restantes 5 parmetros os valores padro definidos nas constantes:
CONSTRUCTOR TPlotter.Create
(cPosition : TPoint; cSize : TSize);
begin
self.Create(cPosition, cSize,
ZoomStandard, DefaultGrid,
DefaultAxes, DefaultFunctionLine,
DefaultPlotterBackColor);
end;
No sero criados destrutores j que, para esta classe, bastar utilizar o Free,
visto que esta herda da classe genrica TObject.
54
Revista PROGRAMAR
SetFillStyle(estilo,
cor);
Descrio
Define o estilo de preenchimento de todos os mtodos
da famlia.
55
Revista PROGRAMAR
counter * self.DistBetweenGrid.X,
self.Position.Y + Self.Size.cy);
Inc(counter, 1);
until counter >= self.Zoom.XMax;
// linhas horizontais:
counter := round(self.Zoom.YMin);
repeat
Line(self.Position.X,
self.Center.Y - counter *
self.DistBetweenGrid.Y,
self.Position.X + self.Size.cx,
self.Center.Y - counter *
self.DistBetweenGrid.Y);
Inc(counter, 1);
until counter >= self.Zoom.YMax;
end;
Mais uma srie de mtodos da unit Graph foram utilizados:
Mtodo & sintaxe
SetColor(cor);
SetLineStyle(estilo,
padro, espessura);
Descrio
Define a cor das linhas de todos os mtodos da famlia.
Cor do tipo Word, define qual a cor.
Define o estilo das linhas de todos os mtodos da
famlia.
56
Revista PROGRAMAR
57
Revista PROGRAMAR
PUBLIC
procedure Refresh;
Este mtodo simplesmente limpa a actual Plotter e desenha-a toda do zero:
PROCEDURE TPlotter.Refresh;
begin
self.Clear;
self.Show;
end;
58
Revista PROGRAMAR
PROCEDURE TPlotter.Relocate
(cNewPosition : TPoint);
begin
self.Clear;
self.vPosition := cNewPosition;
self.Show;
end;
PROCEDURE TPlotter.Relocate
(cX, cY : LongInt); OVERLOAD;
var NewPos : TPoint;
begin
NewPos.X := cX;
NewPos.Y := cY;
self.Relocate(NewPos);
end;
59
Revista PROGRAMAR
Para este processo, o clculo deve entrar com o Zoom, a dimenso e o centro
do grfico, para alm do valor real da funo a cada X. Implementando estes princpios,
deveremos chegar ao seguinte cdigo:
PROCEDURE TPlotter.LoadFunction
(fn : TRealFunction);
var abscissa : real;
counter : integer;
step : real;
begin
// grava funo:
self.vFunction := fn;
// inicia contadores:
counter := self.Position.X;
abscissa := self.Zoom.XMin;
// define passo de incrementao:
step := self.Zoom.XScl /
self.DistBetweenGrid.X;
repeat
// clculo do ponto:
self.vValuesOfFunction[counter]:=
self.vCenter.Y self.vFunction(abscissa) *
(self.DistBetweenGrid.Y /
self.Zoom.YScl);
// incrementa contadores:
abscissa := abscissa + step;
Inc(counter, 1);
until counter >= self.Position.X +
60
Revista PROGRAMAR
self.Size.cx;
end;
Tendo em conta o centro do grfico, vamos andar para trs ou para a frente
deste consoante o valor real da funo: isto definido atravs de uma
subtraco;
self.vCenter.Y - self.vFunction(abscissa)
Contudo, o valor real da funo deve ser multiplicado por um factor que nos
indique a verdadeira posio, em pixis, do ponto. Assim sendo, se a distncia
entre linhas da grelha est para a escala, ento 1 pixel est para o factor que
procuramos:
Distncia Escala
1
Factor
Este factor , ento:
self.DistBetweenGrid.Y / self.Zoom.YScl
E, assim, temos o clculo do ponto da funo na Plotter.
Depois de carregar a funo, obviamente queremos que esta seja desenhada,
ou representada (DrawFunction).
PUBLIC
procedure DrawFunction;
A funo s ser desenhada se a propriedade FunctionLine.Visible estiver
definida a True, e s ser desenhada a parte do grfico que estiver dentro da rea da
Plotter, pelo que este controlo ser feito dentro do prprio ciclo que ir desenhar a
funo, e esta anlise ter de ser, obviamente, ponto a ponto. A vantagem de ter
calculado os pontos aquando o LoadFunction reside no facto de se evitarem mais
clculos no desenho da funo, bastando fazer o ouput das linhas que unem os pontos.
61
Revista PROGRAMAR
PROCEDURE TPlotter.DrawFunction;
var counter : integer;
begin
if self.FunctionLine.Visible
and not(self.vFunction=nil)
then begin
// define estilo da linha
SetLineStyle(self.FunctionLine.Style,
0, self.FunctionLine.Thickness);
// define cor
SetColor(self.FunctionLine.Color);
// desenha a funo:
for counter := self.Position.X to
(self.Position.X + self.Size.cx - 2)
do begin
{evita que o grfico seja desenhado
fora da rea da Plotter}
if not ((round(
self.vValuesOfFunction[counter+1]) <
self.Position.Y) or (round(
self.vValuesOfFunction[counter+1]) >
self.Position.Y + self.Size.cy))
then begin
//usa directamente vValuesOfFunction
Line(counter, round(
self.vValuesOfFunction[counter]),
counter+1, round(
self.vValuesOfFunction[counter+1]));
end;
end;
end;
end;
Como ser bvio, grande parte deste cdigo o conjunto de todas as condies
de controlo para o desenho da funo dentro da rea. O verdadeiro acto de desenhar a
funo est quase no fim, aquando a utilizao do mtodo Line.
De seguida, poderemos querer limpar a funo (ClearFunction), ou seja, voltar
a ter a Plotter sem qualquer funo desenhada.
PUBLIC
procedure ClearFunction;
62
Revista PROGRAMAR
63
Revista PROGRAMAR
PUBLIC
procedure Resize(cNewSize:TSize);
procedure Resize(ccx, ccy:LongInt);
overload;
Como a alterao da dimenso implica uma nova distncia entre as linhas das
grelhas, bem como a mudana do centro do grfico, estes devem ser recalculados. Do
mesmo modo, a funo ter de se adaptar, pelo que a soluo recarregar a funo
fazendo um LoadFunction da funo que a Plotter j tiver.
PROCEDURE TPlotter.Resize
(cNewSize : TSize);
begin
self.vSize := cNewSize;
self.CalcDistGridAndCenter;
if not(self.vFunction = nil) then
self.LoadFunction(self.vFunction);
self.Refresh;
end;
PROCEDURE TPlotter.Resize
(ccx, ccy : LongInt); OVERLOAD;
var NewSize : TSize;
begin
NewSize.cx := ccx;
NewSize.cy := ccy;
self.Resize(NewSize);
end;
64
Revista PROGRAMAR
PUBLIC
procedure Rezoom(cNewZoom : TZoom);
O zoom, tal como a dimenso, obriga a um reclculo da distncia entre as linhas
da grelha e do centro do grfico, bem como ao reclculo dos pontos da funo.
PROCEDURE TPlotter.ReZoom
(cNewZoom : TZoom);
begin
self.vZoom := cNewZoom;
self.CalcDistGridAndCenter;
if not(self.vFunction = nil) then
self.LoadFunction(self.vFunction);
self.Refresh;
end;
65
Revista PROGRAMAR
PROGRAM Teste;
uses Graph,
CalcGraph,
SysUtils,
Windows;
FUNCTION f(x:real):real;
begin
f := sqr(x)-8;
end;
CONST
InitPos:TPoint=(X:10; Y:10);
InitSize:TSize=(cx:400; cy:200);
PosZoom:TZoom =
(XMin:-4; XMax:8; XScl:2;
YMin:-9; YMax:2; YScl:1);
VAR Driver, Modus : SmallInt;
Plot : TPlotter;
BEGIN
TRY
TRY
// inicia rea grfica
DetectGraph(Driver, Modus);
InitGraph(Driver, Modus, '');
// cria Plotter
Plot:=
TPlotter.Create(InitPos,InitSize);
// altera propriedades
Plot.BackColor:=Blue;
Plot.Grid.Color:=Black;
Plot.Axes.Color:=Yellow;
Plot.Axes.Thickness:=NormWidth;
// desenha a Plotter
Plot.Show;
// carrega a funo
Plot.LoadFunction(@f);
// altera cor da funo
Plot.FunctionLine.Color:=LightRed;
// desenha funo
Plot.DrawFunction;
readln; // pausa
Plot.Resize(700,400);
Plot.ReZoom(PosZoom);
Plot.DrawFunction;
66
Revista PROGRAMAR
EXCEPT
ON ex:exception do begin
MessageBox(0,@ex.message[1],'Erro!',
MB_ICONASTERISK);
end;
END
FINALLY
write('FIM.'); readln;
// liberta recursos da Plotter
Plot.Free;
CloseGraph;
END
END.
Neste programa crimos uma Plotter atravs da varivel Plot, ou seja, uma
instncia da classe TPlotter. Os seus valores padro para as vrias propriedades foram
alterados:
A cor de fundo foi definida a azul, em vez da predefinio (verde);
A cor da grelha foi definida a preto e no a cinzento-escuro;
A cor dos eixos passou a ser amarela, e a sua espessura passou a ser de 1
pixel;
A cor da funo passou para vermelho-vivo (LightRed) em vez de vermelhoescuro (Red).
Aps a alterao destas propriedades, o resultado final (Figuras 7 e 8)
diferente do inicialmente representado na Figura 1, que seria o output gerado caso se
mantivesse a predefinio.
67
Revista PROGRAMAR
68
Revista PROGRAMAR
Resumo
Com este artigo recorremos unit Graph para conceber uma Plotter, tendo a
sua implementao sido feita com o uso do OOP, ou seja, o paradigma da Programao
Orientada aos Objectos.
Desta forma ser possvel ter, numa s rea grfica, vrias Plotters com
caractersticas diferentes e a trabalhar com funes diferentes, mas totalmente
independentes umas das outras para tal, bastar criar diferentes instncias da classe
TPlotter.
Apesar de no se ter aprofundado a unidade na qual nos baseamos, ficou claro
que com pouco se pode fazer muito, e com meia dzia de mtodos da Graph uma
Plotter totalmente funcional e personalizvel foi construda.
Destacam-se, ento, os mtodos Bar, Line, SetFillStyle e SetLineStyle, assim
como as constantes NormWidth, ThickWidth, EmptyFill, SolidFill, SolidLn e
DashedLn.
Sobre o autor
Igor Nunes
Curioso na rea das tecnologias e em especial da programao, tem um grande
interesse pelas tecnologias de ensino da Texas Instruments e pelo TI-Basic, alm de
ter uma saudvel relao com o Pascal. conhecedor de outras linguagens de
programao, como VB.NET.
No P@P, membro da Wiki Team e Moderador Local dos quadros de Pascal e
Delphi/Lazarus. Escreveu o novo Tutorial de Pascal, recentemente disponibilizado em
PDF.
69
Revista PROGRAMAR
42 Edio
70
Revista PROGRAMAR
71
Revista PROGRAMAR
V := I64;
V := S;
end.
Contudo, h uma excepo para o caso das arrays. Apesar de no ser possvel
atribuir directamente uma array a uma varivel do tipo variant, esta poder conter uma
array desde que seja criada pela funo VarArrayCreate.
A unit system contm um pequeno conjunto de procedimentos e funes que
permitem que uma varivel variant se torne numa array e que esta seja manipulada.
Analisemos a situao atravs do seguinte cdigo:
var i : integer;
v : variant;
begin
v:=VarArrayCreate([1,10], varInteger);
for i:=1 to 10 do
v[i]:=i;
VarArrayRedim(v,20);
for i:=11 to 20 do
v[i]:=i;
for i:=1 to 20 do
write(v[i], ',');
end.
VarArrayCreate uma funo que permite a criao de uma array numa varivel
variant, onde lhe fornecida por argumento a dimenso (neste caso seria uma
array[1..10]) e qual o tipo de dados armazenado na array (varInteger uma constante
da System que indica que a array ir armazenar integers).
De seguida, podemos redimensionar a array com o procedimento
VarArrayRedim, onde passamos a varivel variant que representa, neste momento,
uma array, seguida da nova dimenso: neste caso ficaremos com uma array[1..20].
Como possvel observar, a partir do momento em que se cria a array na varivel
variant com este mtodo, podemos aceder aos seus elementos atravs dos seus ndices
como se de uma array tradicional se tratasse.
Uma nota importante acerca das variveis variant que assumem strings: ao
contrrio de uma varivel string, as variants no permitem o acesso aos caracteres da
string atravs dos seus ndices. Se o fizermos, ser levantada uma excepo do tipo
EVariantInvalidArgError.
Um pormenor interessante que pode levantar dvidas prende-se com a
atribuio do valor contido numa varivel variant a outra varivel de tipo esttico. Isto
possvel e traz, claro, alguns pormenores relacionados com a compatibilidade dos tipos
de dados e a converso implcita que da poder surgir. Vejamos o seguinte exemplo:
72
Revista PROGRAMAR
var V : variant;
I : integer;
begin
V := 42;
I := V;
end.
Estamos a atribuir a uma varivel do tipo integer o valor de uma varivel variant
que representa, no momento, uma string. Esta atribuio vlida porque a string 42
representa um nmero inteiro vlido, pelo que ocorre a converso implcita para
integer. Contudo, se a string fosse, por exemplo, 42A, a converso seria impossvel,
ocorrendo ento uma excepo do tipo EConvertError.
Este tipo de dados no propriamente eficiente, como lgico. O programa, em
runtime, dever avaliar constantemente os valores que so atribudos e manipulados
nas variveis variant e isto implica processamento extra. Portanto, havendo tipos
estticos que so mais eficientes, o tipo variant dever ser utilizado apenas quando
estritamente necessrio.
Tipos genricos
J do conhecimento geral que a famlia de linguagens Pascal tem pleno
suporte ao paradigma OOP com as suas variantes Object Pascal e Delphi. Contudo, o
que poucos sabero que as classes podem ser genricas.
Regra geral, uma classe ter em si uma varivel, ou conjunto de variveis, onde
os seus dados esto armazenados e os quais so manipulados com os mtodos da
classe. Se pretendemos, por exemplo, guardar informaes acerca de carros teramos
uma lista do tipo TCarro, se pretendermos guardar informaes acerca de pessoas
teremos uma lista do tipo TPessoa. Podemos j constatar que ambos estes exemplos
tm algo em comum: so listas com o objectivo de registar e manipular dados relativos
a objectos. Ora, ser natural pensar que os mtodos que realizam essas operaes so
no s equivalentes como muito provavelmente iguais a nica diferena est no tipo
dos objectos.
Deparamo-nos, portanto, com o seguinte dilema: pretende-se criar uma classe
que possa lidar com um conjunto de objectos, mas estes objectos podem ser de
qualquer tipo. O primeiro pensamento do programador ser preciso de implementar
uma classe para cada tipo de dados.
E aqui onde o programador estar equivocado.
Uma classe genrica uma classe que est preparada para trabalhar com
dados de qualquer tipo, tipo este que informado classe por um processo designado
especializao. Portanto, h duas fases:
1. Implementao da classe genrica;
2. Especializao da classe genrica com a criao de um novo tipo de dados.
73
Revista PROGRAMAR
Ou seja, criamos a classe para que ela trabalhe com um tipo de dados genrico
T, e mais tarde o programador que precisar da classe especializa-a, indicando o que
que este tipo T dever ser: poder ser um Integer, um Real, uma String, uma Array
ou at mesmo uma classe!
A sintaxe da declarao de uma classe genrica em Free Pascal a seguinte:
type generic TClasse<T> = class
A keyword generic indica que a classe genrica, e frente do nome da
classe, entre os sinais de maior e menor, damos o nome ao tipo genrico, neste caso
T (que o nome dado mais comum).
Quando o programador necessitar da classe, dever indicar um novo tipo de
dados onde a classe genrica especializada, sendo a sintaxe a seguinte:
type TClasseEspecializada = specialize TClasse<Tipo>;
Tipo no poder ser uma classe genrica, mas poder ser uma classe
74
Revista PROGRAMAR
75
Revista PROGRAMAR
SetLength(self.data, limit);
self.top := 0;
end;
Como vimos antes, o programador pode definir um tamanho inicial para a stack
ou no, e no caso de no o definir, esta ter um tamanho por defeito, indicado com a
constante privada INITSIZE.
Podemos tambm j definir a funo IsEmpty pela sua simplicidade:
simplesmente verificamos se top igual a zero, o que indica que no h elementos na
stack.
function TStack.IsEmpty:boolean;
begin
IsEmpty := (self.top = 0);
end;
De seguida vamos implementar o mtodo push que vai permitir adicionar um
elemento stack. Como foi referido, se a array no tiver tamanho suficiente, a sua
dimenso ser duplicada:
procedure TStack.Push(value : T);
begin
inc(self.top);
if self.top > length(self.data) then
SetLength(self.data, length(self.data)*2);
self.data[self.top-1] := value;
end;
Ou seja, aumentamos top em uma unidade, verificamos o tamanho da array e
atribumos o valor, fornecido no argumento value.
Por fim, s nos resta implementar a funo pop. Esta funo vai retornar o ltimo
valor da stack, decrementando o indicador top. No caso de o programador realizar um
pop indevido (quando a stack est vazia), ele dever lidar com o erro que da surgir
h um mtodo IsEmpty que o auxilia, pelo que no est no mbito do artigo a gesto
deste erro. Traduzindo em cdigo:
function TStack.Pop : T;
begin
Pop := self.data[self.top-1];
dec(self.top);
end;
Temos, portanto, a nossa stack implementada. S nos resta test-la.
{$mode objfpc}
program Teste_Stack;
// Cdigo da classe aqui
type TIntegerStack =
76
Revista PROGRAMAR
specialize TStack<Integer>;
var s : TIntegerStack;
i : integer;
begin
s := TIntegerStack.Create(5);
for i in [1..10] do
s.Push(i);
while not s.IsEmpty do
write(s.Pop, ', ');
writeln('FIM');
s.Free;
readln;
end.
Especializmos a stack para lidar com dados do tipo Integer, e indicmos que
estamos a prever que esta tenha 5 elementos, o que, neste caso, no se concretiza
vamos testar a capacidade de autoexpanso da stack. Fizemos, portanto, push de
nmeros de 1 a 10 por ordem, e de seguida ordenmos o esvaziamento completo por
pop encaixado numa estrutura while que termina quando a stack estiver vazia (verificado
pela funo IsEmpty), escrevendo os valores no output. Eis o resultado deste programa:
10, 9, 8, 7, 6, 5, 4, 3, 2, 1, FIM
Como podemos constatar, a stack est a cumprir o seu propsito LIFO (Last In
First Out) o ltimo elemento a entrar o primeiro a sair , bem como foi capaz de se
redimensionar de forma a acomodar todos os valores.
Por curiosidade, podamos ter especializado a classe com o tipo variant ou
seja, a stack podia literalmente conter elementos de qualquer tipo de dados simples.
Isto revela, em ltima instncia, a versatilidade dos tipos variantes e genricos no s
em separado como em conjunto.
Sobre o autor
Igor Nunes
Curioso na rea das tecnologias e em especial da programao, tem um grande
interesse pelas tecnologias de ensino da Texas Instruments e pelo TI-Basic, alm de
ter uma saudvel relao com o Pascal. conhecedor das bases de outras linguagens
de programao, como VB.NET, Python e Haskell.
No P@P, membro da Wiki Team e Moderador Global. Escreveu o novo Tutorial
de Pascal, disponibilizado em PDF.
77
Revista PROGRAMAR
44 Edio
78
Revista PROGRAMAR
79
Revista PROGRAMAR
80
Revista PROGRAMAR
81
Revista PROGRAMAR
2
2
2
2
2
*
*
*
*
*
2
7
5
9
4
= 4
= 14
= 10
= 18
= 8
82
Revista PROGRAMAR
83
Revista PROGRAMAR
84
Revista PROGRAMAR
Overload do operador IN
Desde a verso 2.6.0 do Free Pascal Compiler que o operador IN tambm pode
ser overloaded. Este operador verifica, por defeito, se um determinado valor pertence a
um set. No entanto, ele no tem a capacidade de fazer esta verificao com um array.
Desta forma, vamos implementar um overload que o permita:
operator in (n : integer; list : TIntegerArray)
res : boolean;
var elem : integer;
begin
res := false;
for elem in list do
if n = elem then begin
res := true;
break;
end;
end;
85
Revista PROGRAMAR
Concluso
Consideremos a implementao de todos estes overloads numa unit
denominada operover, tal como foi referido ao longo do artigo. Vamos test-la com o
seguinte programa:
{$mode objfpc}
program artigo44;
uses operover;
var list1 : TIntegerArray = nil;
list2 : TIntegerArray = nil;
i : integer;
begin
list1 := [2,7,5,9,4];
list2 := -list1 * 2;
for i := Low(list1) to High(list1) do
writeln('2 * ',-list1[i]:2,' = ',list2[i]:3);
writeln('list1 > list2? ':16, list1 > list2);
writeln('list1 <= list2? ':16, list1 <= list2);
writeln('-4 in list2? ':16, -4 in list2);
end.
O output deste programa o seguinte:
86
Revista PROGRAMAR
2 * -2 =
2 * -4 =
2 * -5 =
2 * -7 =
2 * -9 =
list1 >
list1 <=
-4 in
-4
-8
-10
-14
-18
list2? TRUE
list2? FALSE
list2? TRUE
Como podemos constatar, apesar do set ter sido fornecido numa ordem
aleatria, os dados foram colocados por ordem crescente, isto por causa do modo como
funciona o tipo de dados set. Podemos portanto concluir que os operadores esto a
funcionar conforme era a nossa inteno.
Desta forma terminamos esta incurso pelo mundo do overloading de
operadores em Free Pascal. Basicamente qualquer tipo de dados pode ser fornecido a
um operador, desde que seja feito o seu overload. Esta uma ferramenta que permite
tornar a linguagem muito mais expressiva, compacta e at intuitiva.
87
Revista PROGRAMAR
45 Edio
88
Revista PROGRAMAR
89
Revista PROGRAMAR
90
Revista PROGRAMAR
Como primeira nota, h que referir que estruturas no podem ser passadas
nestes arrays. Apenas tipos de dados simples (tipos numricos, alfanumricos e
apontadores), objectos, classes e interfaces podem.
Vamos criar um procedimento que recebe um array of const e nos d
informaes acerca dos argumentos recebidos:
procedure Foo(args : array of const);
Quando o procedimento recebe este array, ocorre uma converso dos seus
elementos para um record variante com caractersticas particulares. Segundo a
documentao do Free Pascal, esta a sua definio:
Type
Ptrint = LongInt;
PVarRec = ^TVarRec;
TVarRec = record
case VType : Ptrint of
vtInteger
:
(VInteger: Longint);
vtBoolean
:
(VBoolean: Boolean);
vtChar
:
(VChar: Char);
vtWideChar
:
(VWideChar: WideChar);
vtExtended
:
(VExtended: PExtended);
vtString
:
(VString: PShortString);
vtPointer
:
(VPointer: Pointer);
vtPChar
:
(VPChar: PChar);
vtObject
:
(VObject: TObject);
vtClass
:
(VClass: TClass);
vtPWideChar :
(VPWideChar: PWideChar);
vtAnsiString :
(VAnsiString: Pointer);
vtCurrency
:
(VCurrency: PCurrency);
vtVariant
:
(VVariant: PVariant);
vtInterface :
(VInterface: Pointer);
vtWideString :
(VWideString: Pointer);
vtInt64
:
(VInt64: PInt64);
vtQWord
:
(VQWord: PQWord);
end;
91
Revista PROGRAMAR
Portanto, cada elemento ser um record no qual o campo VType indica qual o
tipo de dados do argumento, e conforme o valor deste campo haver um segundo que
contm o valor do argumento. Por exemplo, se VType for vtString, ento o argumento
do tipo ShortString (comummente designado apenas como string), e o seu valor pode
ser acedido atravs do campo VString.
Uma vez que cada elemento pode ter um tamanho em bytes diferente dos
restantes, o armazenamento dos dados referentes aos argumentos no pode ser feito
da forma tradicional: um array clssico tem os seus elementos armazenados em reas
contguas de memria em que cada elemento ocupa exactamente N bytes. Neste caso,
cada argumento ter a sua localizao num bloco da memria distinto. Isto explica o
facto de haver um apontador (o tipo PVarRec).
Conclui-se que um array of const , em ltima instncia, um array of TVarRec
dentro do procedimento ou funo. Mas ateno, nunca se deve declarar o argumento
desta forma!
procedure Foo(args : array of TVarRec);
Para tal, ser til controlar quantos argumentos foram passados. Esta
informao pode ser obtida com a funo Length. Caso no haja argumentos, iremos
mostrar a mensagem Sem argumentos.
procedure Foo(args : array of const);
var i : smallint;
begin
if Length(args) > 0 then
// anlise dos argumentos
else
writeln('Sem argumentos.');
end;
Para proceder anlise dos argumentos, iremos iterar pelos elementos do array.
Sendo este um open array, cuja dimenso desconhecemos, necessitaremos das
funes Low e High que devolvem, respectivamente, os ndices menor e maior do array
(isto , as posies do primeiro e ltimo elemento).
for i:=Low(args) to High(args) do
// anlise dos argumentos
92
Revista PROGRAMAR
Como foi referido, os elementos do array so convertidos ao record infraapresentado, pelo que possuem campos. Naturalmente, uma estrutura de deciso
case-of ir analisar o valor do campo vtype, informando acerca do tipo de dados do
argumento. Caso no seja conhecido, mostrar-se- a mensagem desconhecido.
Comecemos por analisar um caso simples: o argumento um Integer. Desta forma,
vtype ir assumir o valor vtInteger (uma constante do tipo LongInt):
case args[i].vtype of
vtinteger :
writeln('Integer = ',
args[i].vinteger);
else
writeln('desconhecido!');
end
93
Revista PROGRAMAR
vtString :
writeln('ShortString = ',
args[i].VString^);
94
Revista PROGRAMAR
writeln('Boolean = ',
args[i].vboolean);
vtchar :
writeln('Char = ',
args[i].vchar);
vtextended :
writeln('Extended = ',
args[i].VExtended^:0:10);
vtString :
writeln('ShortString = ',
args[i].VString^);
vtAnsiString :
writeln('AnsiString = ',
AnsiString(Args[i].VAnsiString));
vtPointer :
writeln('Pointer = ',
Longint(Args[i].VPointer));
vtObject :
writeln('Object = ',
Args[i].VObject.Classname);
vtClass :
writeln('Class = ',
Args[i].VClass.Classname);
else
writeln('desconhecido!');
end
else
writeln('Sem argumentos.');
writeln;
end;
95
Revista PROGRAMAR
Sem argumentos.
AnsiString = Igor Nunes
Integer = 31
Pointer = 471399
Pointer = 0
Boolean = FALSE
Char = K
ShortString = variavel s
Class = TStringList
Extended = 3.1400000000
Repare-se que a varivel s, do tipo string, foi considerada uma ShortString. Isto
acontece uma vez que, por defeito, o Free Pascal considera que o tipo de dados string
se refere ao tipo ShortString.
Por outro lado, uma string escrita directamente no argumento considerada uma
AnsiString. Mais uma vez, o Free Pascal assume que uma string escrita directamente
como argumento deste tipo, uma vez que, segundo as actuais regras, estas strings
no tm um tamanho definido e s podem assumir caracteres ASCII por defeito.
Como ltima referncia, de notar o output para a varivel sl: o procedimento
informou-nos que esta uma classe, mais propriamente a classe TStringList.
96
Revista PROGRAMAR
97