Escolar Documentos
Profissional Documentos
Cultura Documentos
87/10/18
(revisto em 91/11/10)
Pedro Guerreiro
Departamento de Informática
Faculdade de Ciências e Tecnologia
Universidade Nova de Lisboa
Programar em COBOL, nos tempos que correm, pode parecer uma per-
da de tempo. Isso é verdade, até certo ponto. Mas, encarando as coisas de ou-
tro modo, podemos retirar bastante gozo desse exercício. Por um lado, há o
gosto um tanto perverso de fazer precisamente o contrário daquilo que as ca-
beças bem-pensantes sabiamente recomendam. Depois, há no COBOL algo de
kitsch, de irremediavelmente antiquado, de confrangedoramente fora de mo-
da, que lhe dá um encanto inigualável. Os programas COBOL bem feitos (ou
seja, os que nós fazemos) nunca deixam de nos surpreender: “como é que se
consegue fazer um programa destes com uma linguagem destas?”. Finalmen-
2
te, resolver um problema em COBOL é como fazer uma viagem num Rolls-
-Royce dos anos vinte: grande, pesado, difícil de manobrar, sedento de gasoli-
na, mas com incomparavelmente mais classe que os pequenos carritos de hoje
em dia, por muito rápidos, confortáveis e económicos que sejam…
2. Características gerais
3
Finalmente, a abundância de possibilidades para definir os “outputs”
(uma concessãozita ao mau-gosto…) é muito apreciada na produção de todo o
tipo de documentos comerciais e administrativos: recibos, facturas, mapas,
etc. (Só não se percebe bem por que razão aparece sempre tudo em maiúscu-
las, e sem acentos, e numas folhas monstruosas, difíceis de consultar…)
4
ENVIRONMENT DIVISION.
INPUT-OUTPUT SECTION.
FILE-CONTROL.
SELECT F-ACT ASSIGN TO ID-F-ACT.
DATA DIVISION.
WORKING-STORAGE SECTION.
77 ID-F-ACT PIC X(32).
5
Quanto às linhas de continuação, convém referir que as instruções
COBOL podem passar de umas linhas para as outras, sem nenhuma indica-
ção especial. O hífen na coluna 7 usa-se, por exemplo, quando se quer “partir”
um identificador ou uma cadeia de caracteres. No entanto, o melhor é organi-
zar as coisas de modo a evitar essas situações, porque as regras de utilização
das linhas de continuação não são assim lá muito “ortogonais”…
DATA DIVISION.
FILE SECTION.
FD F-ACT.
01 ACT-REG.
02 ACT-TIPO-OP PIC A.
02 ACT-INFO.
03 ACT-NUM-FUNC PIC 9(4).
03 ACT-NOME.
04 ACT-APELIDO PIC X(16).
04 ACT-RESTO-NOME PIC X(32).
03 ACT-BI PIC 9(8).
03 ACT-NO-CONTRIB PIC 9(9).
03 ACT-NASC.
04 ACT-NASC-ANO PIC 99.
04 ACT-NASC-MES PIC 99.
04 ACT-NASC-DIA PIC 99.
03 ACT-MORADA.
04 ACT-MORADA-1 PIC X(30).
04 ACT-MORADA-2 PIC X(30).
04 ACT-MORADA-3 PIC X(20).
6
uma abreviatura de PIC 9(2); e PIC A, denotando um carácter que só pode
ser uma letra ou um espaço. Em geral, portanto, poderão especificar-se ca-
deias de caracteres arbitrários, de letras e espaços, ou de algarismos apenas,
com comprimento à escolha, na forma PIC X(...), PIC A(...) e PIC
9(...), respectivamente.
Os valores digitados não devem ser aceites sem uma validação prelimi-
nar. Não se pretende um sistema muito sofisticado (que, para ser feito como
deve ser, exigiria outras técnicas de programação) pelo que apenas se verifi-
cará se os campos ficam com valores de tipos apropriados, se a data é plausí-
7
vel, e se a letra que vai para o campo ACT–TIPO–OP identifica bem uma das
três operações possíveis.
Programa
Inicialização Finalização
não-há-mais-ops
lote-ops
8
lote-ops
criações
não-há-mais-criações
escrever
registo mensagem
9
ma. É costume chamar-se-lhe a “árvore programática” do programa. Vamos
usá-la como guia para a redacção do programa COBOL:
Programa
Inicialização Finalização
não-há-mais-ops
lote-ops
•
não-há-mais-criações
escrever
registo mensagem
10
3.4.1. Nível superior
PROCEDURE DIVISION.
ESTRUTURA SECTION.
PROGRAMA.
PERFORM INICIALIZACAO.
MOVE 0 TO B-NAO-HA-MAIS-OPS.
PERFORM LOTE-OPS UNTIL NAO-HA-MAIS-OPS.
PERFORM FINALIZACAO.
STOP RUN.
PERFORM S UNTIL B
corresponde a
11
while not B do S
MOVE A TO B
equivale a
B := A
77 B-NAO-HA-MAIS-OPS PIC 9.
77 B-NAO-HA-MAIS-OPS PIC 9.
88 NAO-HA-MAIS-OPS VALUE 1.
12
77 B-X PIC...
88 X VALUES v1, v2, ..., vn.
corresponderia em Pascal a:
function x: boolean;
begin x := b_x in [v1, v2, ..., vn] end;
05 P-SEXO PIC A.
Se se tivesse definido:
05 P-SEXO PIC A.
88 EH-MULHER VALUE "M".
88 EH-HOMEM VALUE "H".
13
gramas COBOL são legíveis, mesmo para quem perceba pouco de programa-
ção…
LOTE-OPS.
PERFORM QUAL-OP.
IF OP-CRIACOES PERFORM CRIACOES
ELSE IF OP-REMOCOES PERFORM REMOCOES
ELSE IF OP-MODIFICACOES PERFORM MODIFICACOES
ELSE MOVE 1 TO B-NAO-HA-MAIS-OPS.
14
77 TIPO-OP PIC X.
88 OP-OK VALUES "C", "R", "M", "F".
88 OP-CRIACOES VALUE "C".
88 OP-REMOCOES VALUE "R".
88 OP-MODIFICACOES VALUE "M".
QUAL-OP.
MOVE SPACE TO TIPO-OP.
PERFORM QUAL-OP-BIS UNTIL OP-OK.
QUAL-OP-BIS.
DISPLAY "C-Criações, R-Remoções, M-Modificações".
DISPLAY "(F-Fim, para acabar)".
ACCEPT TIPO-OP.
IF NOT OP-OK DISPLAY "Operação mal especificada".
77 B-NAO-HA-MAIS-CRIACOES PIC 9.
88 NAO-HA-MAIS-CRIACOES VALUE 1.
77 B-TUDO-OK PIC 9.
88 TUDO-OK VALUE 1.
MAIS-CRIACOES.
DISPLAY "Mais criações? (S/N) ".
ACCEPT RESP.
IF RESP = "S" MOVE 0 TO B-NAO-HA-MAIS-CRIACOES
ELSE MOVE 1 TO B-NAO-HA-MAIS-CRIACOES.
Mas calma! Se vai ser preciso mais uma variável RESP para aceitar a
resposta e depois testá-la para definir o valor “booleano” de B–NAO–HA–MAIS–
CRIACOES, o melhor é modificar a definição desta e evitar os intermediários:
77 B-NAO-HA-MAIS-CRIACOES PIC X.
88 NAO-HA-MAIS-CRIACOES VALUE "N".
MAIS-CRIACOES.
DISPLAY "Mais criações? (S/N) ".
ACCEPT B-NAO-HA-MAIS-CRIACOES.
Podemos antecipar que, por este caminho, vão ser precisas mais duas
variáveis do género de B–NAO–HA-MAIS-CRIACOES, uma para as remoções,
outra para as modificações. Mas, em vez de atravancar o programa com va-
riáveis booleanas, não será má ideia partilhar uma entre todas essas opera-
ções, visto que a sua utilização não se sobrepões no tempo. Temos que lhe dar
um nome neutro:
77 B-NAO-HA-MAIS-REGS PIC X.
88 NAO-HA-MAIS-REGS VALUE "N".
16
Haverá sim três parágrafos diferentes, para a continuação da introdu-
ção de registos da mesma classe, porque as mensagens afixadas no ecrã não
são as mesmas:
MAIS-CRIACOES.
DISPLAY "Mais criações? (S/N) ".
ACCEPT B-NAO-HA-MAIS-REGS.
...
MAIS-REMOCOES.
DISPLAY "Mais remoções? (S/N) ".
ACCEPT B-NAO-HA-MAIS-REGS.
...
MAIS-MODIFICACOES.
DISPLAY "Mais modificações? (S/N) ".
ACCEPT B-NAO-HA-MAIS-REGS.
77 B-TUDO-OK PIC X.
88 TUDO-OK VALUE "S".
CONFIRMACAO-CRIACOES.
DISPLAY "---Dados referentes a um novo funcionário---".
PERFORM AFIXAR-DADOS.
DISPLAY "Tudo bem? (S/N) ".
ACCEPT B-TUDO-OK.
17
za. Donde, um esquema iterativo ao nível da aceitação de cada informação
elementar:
OBTER-DADOS-CRIACOES.
MOVE 0 TO B-OK. PERFORM ACEITAR-NUM-FUNC UNTIL OK.
MOVE 0 TO B-OK. PERFORM ACEITAR-APELIDO UNTIL OK.
MOVE 0 TO B-OK. PERFORM ACEITAR-RESTO-NOME UNTIL OK.
MOVE 0 TO B-OK. PERFORM ACEITAR-BI UNTIL OK.
MOVE 0 TO B-OK. PERFORM ACEITAR-NO-CONTRIB UNTIL OK.
MOVE 0 TO B-OK. PERFORM ACEITAR-NASC UNTIL OK.
MOVE 0 TO B-OK. PERFORM ACEITAR-MORADA UNTIL OK.
77 B-OK PIC 9.
88 OK VALUE 1.
Temos agora que decidir quanto às variáveis que vão receber os valores
introduzidos. Em primeira aproximação poderíamos usar directamente os
campos do registo ACT-REG. Mas isso não pode ser, pois correríamos o risco
de o programa funcionar mal se, por exemplo, o utilizador se enganasse e in-
cluísse uma letra no número de funcionário. Uma alternativa seria dispor de
uma série de variáveis avulso, uma para cada coisa. Mas então porque não
agrupá-las numa variável estruturada cuja forma, afinal, até poderá ser com-
patível com a de ACT-REG? Chamemos-lhe ED-REG (uma sigla para “registo
de entrada de dados”). A sua declaração virá na secção WORKING-STORAGE:
18
WORKING-STORAGE SECTION.
77 ...
01 ED-REG.
02 ED-TIPO-OP PIC A.
02 ED-INFO.
03 ED-NUM-FUNC PIC X(4).
03 ED-NOME.
04 ED-APELIDO PIC X(16).
04 ED-RESTO-NOME PIC X(32).
03 ED-BI PIC X(8).
03 ED-NO-CONTRIB PIC X(9).
03 ED-NASC.
04 ED-NASC-ANO PIC XX.
04 ED-NASC-MES PIC XX.
04 ED-NASC-DIA PIC XX.
03 ED-MORADA.
04 ED-MORADA-1 PIC X(30).
04 ED-MORADA-2 PIC X(30).
04 ED-MORADA-3 PIC X(20).
OBTER-DADOS-CRIACOES.
MOVE TIPO-OP TO ED-TIPO-OP.
MOVE 0 TO B-OK. PERFORM ACEITAR-NUM-FUNC UNTIL OK.
MOVE 0 TO B-OK. PERFORM ACEITAR-APELIDO UNTIL OK.
...
TRATAMENTO SECTION.
ACEITAR-NUM-FUNC.
PERFORM D-A-NUM-FUNC.
IF ED-NUM-FUNC IS NUMERIC MOVE 1 TO B-OK
ELSE DISPLAY "??? valor não numérico".
ACEITAR-APELIDO.
...
SERVISSO SECTION.
D-A-NUM-FUNC.
DISPLAY "Número de funcionário: " NO ADVANCING.
ACCEPT ED-NUM-FUNC.
19
Repare-se em como é feita a validação do valor introduzido: a expressão
IS NUMERIC (o IS é facultativo) é um exemplo de uma condição de classe. A
regra é: para uma cadeia de caracteres S, a condição S IS NUMERIC dá ver-
dade se e só se S for composta exclusivamente por algarismos.
...
ACEITAR-APELIDO.
PERFORM D-A-APELIDO.
IF ED-APELIDO IS ALPHABETIC AND
ED-APELIDO NOT = SPACES MOVE 1 TO B-OK
ELSE DISPLAY "??? Apelido inválido".
...
D-A-APELIDO.
DISPLAY "Apelido: " NO ADVANCING.
ACCEPT ED-APELIDO.
Agora, o controlo que se faz é para que o apelido contenha apenas le-
tras e espaços, e pelo menos uma letra. O IS ALPHABETIC é outra condição
de classe, a qual dá verdade apenas quando todos os caracteres são letras
maiúsculas ou espaços. (Se houver algum funcionário com um nome esquisito,
com apóstrofo, por exemplo, o nome terá que ser reortografado). O NOT = é o
operador de desigualdade em COBOL.
01 DDATA.
02 DIA PIC XX.
02 MES PIC XX.
02 ANO PIC XX.
77 B-OK-DATA PIC 9.
88 OK-DATA VALUE 1.
20
LER-DATA.
ACCEPT DDATA.
IF DDATA IS NUMERIC AND
DIA NOT < "01" AND NOT > "31" AND
MES NOT < "01" AND NOT > "12" MOVE 1 TO B-OK-DATA
ELSE MOVE 0 TO B-OK-DATA.
01 DDATA.
02 ANO PIC XX.
02 MES PIC XX.
88 MES-OK VALUES "01" THRU "12".
02 DIA PIC XX.
88 DIA-OK VALUES "01" THRU "31".
...
LER-DATA.
ACCEPT DDATA.
IF DDATA IS NUMERIC AND DIA-OK AND MES-OK
MOVE 1 TO B-OK-DATA
ELSE MOVE 0 TO B-OK-DATA.
ACEITAR-NASC.
DISPLAY "Data de nascimento (DDMMAA): " NO ADVANCING.
PERFORM LER-DATA.
IF OK-DATA THEN MOVE ANO TO ED-NASC-ANO
MOVE MES TO ED-NASC-MES
MOVE DIA TO ED-NASC-DIA
MOVE 1 TO B-OK
ELSE DISPLAY "??? Data inválida".
21
OBTER-MORADA.
PERFORM D-A-MORADA.
IF ED-MORADA NOT = SPACES MOVE 1 TO B-OK
ELSE DISPLAY "??? a morada não pode ficar em branco"
...
D-A-MORADA.
DISPLAY "Morada -- Primeira linha: ". ACCEPT ED-MORADA-1.
DISPLAY "Morada -- Segunda linha: ". ACCEPT ED-MORADA-2.
DISPLAY "Morada -- Terceira linha: ". ACCEPT ED-MORADA-3.
SERVISSO SECTION.
...
D-ED-REG.
DISPLAY "Func. Num. " ED-NUM-FUNC.
DISPLAY "Apelido: ", ED-APELIDO, " ",
"Outros nomes: ", ED-RESTO-NOME.
DISPLAY "BI: ", ED-BI,
" No.Contrib: ", ED-NO-CONTRIB, " ",
"Data Nasc: ",
ED-NASC-DIA, "/", ED-NASC-MES, "/", ED-NASC-ANO.
DISPLAY "Morada: ", ED-MORADA-1.
DISPLAY " ", ED-MORADA-2.
DISPLAY " ", ED-MORADA-3.
Mas logo notamos que, bem vistas as coisas uma afectação global deve
bastar:
22
Deste modo, o procedimento “escr-act” fica, simplesmente:
ESCR-ACT.
MOVE ED-REG TO ACT-REG.
WRITE ACT-REG.
Tal como em Pascal, existe em Cobol uma maneira para fazer as duas
coisas de uma vez só. Em Pascal seria:
write(f_act, ed_reg)
Em COBOL é:
REMOCOES.
MOVE 0 TO B-NAO-HA-MAIS-REGS.
PERFORM REMOCOES-BIS UNTIL NAO-HA-MAIS-REGS.
REMOCOES-BIS.
DISPLAY "***Remoção de funcionários***".
PERFORM OBTER-DADOS-REMOCOES.
PERFORM CONFIRMACAO-REMOCOES.
IF TUDO-OK PERFORM ESCR-ACT
ELSE DISPLAY "Não confirmado".
PERFORM MAIS-REGS.
OBTER-DADOS-REMOCOES.
MOVE SPACES TO ED-REG.
MOVE TIPO-OP TO ED-TIPO-OP.
MOVE 0 TO B-OK. PERFORM ACEITAR-NUM-FUNC UNTIL OK.
CONFIRMACAO-REMOCOES.
DISPLAY "---Remoção de um funcionário---"
PERFORM D-ED-NUM-FUNC.
DISPLAY "Tudo bem? (S/N) ".
ACCEPT B-TUDO-OK.
D-ED-NUM-FUNC.
DISPLAY "Func. Num. ", ED-NUM-FUNC.
23
Os parágrafos referentes às modificações são quase iguais aos das cria-
ções, apenas com a diferença de se permitir que alguns campos fiquem em
branco.
MODIFICACOES.
MOVE 0 TO B-NAO-HA-MAIS-REGS.
PERFORM MODIFICACOES-BIS UNTIL NAO-HA-MAIS-REGS.
MODIFICACOES-BIS.
DISPLAY "***Modificação de dados de funcionários***".
PERFORM OBTER-DADOS-MODIFICACOES.
PERFORM CONFIRMACAO-MODIFICACOES.
IF TUDO-OK PERFORM ESCR-ACT
ELSE DISPLAY "Não confirmado".
PERFORM MAIS-REGS.
OBTER-DADOS-MODIFICACOES.
MOVE 0 TO B-OK. PERFORM ACEITAR-NUM-FUNC UNTIL OK.
MOVE 0 TO B-OK. PERFORM ACEITAR-APELIDO-MOD UNTIL OK.
MOVE 0 TO B-OK. PERFORM ACEITAR-RESTO-NOME-MOD UNTIL OK.
MOVE 0 TO B-OK. PERFORM ACEITAR-BI-MOD UNTIL OK.
MOVE 0 TO B-OK. PERFORM ACEITAR-NO-CONTRIB-MOD UNTIL OK.
MOVE 0 TO B-OK. PERFORM ACEITAR-NASC-MOD UNTIL OK.
PERFORM ACEITAR-MORADA.
ACEITAR-APELIDO-MOD.
PERFORM D-A-APELIDO.
IF ED-APELIDO IS ALPHABETIC MOVE 1 TO B-OK
ELSE DISPLAY "??? apelido não alfabético".
ACEITAR-NASC-MOD.
DISPLAY "Data de nascimento (DDMMAA): " NO ADVANCING.
PERFORM LER-DATA.
IF DDATA = SPACES MOVE SPACES TO ED-NASC
MOVE 1 TO B-OK
ELSE IF OK-DATA MOVE DIA TO ED-NASC-DIA
MOVE MES TO ED-NASC-MES
MOVE ANO TO ED-NASC-ANO
MOVE 1 TO B-OK
ELSE DISPLAY "??? Data inválida".
24
...
FD F-ACT.
01 ACT-REG PIC X(156).
Tudo junto:
25
INICIALIZACAO.
DISPLAY "Gestão do Pessoal".
DISPLAY "Criação do ficheiro de actalizações".
DISPLAY "Nome do ficheiro de actualizacoes: "
NO ADVANCING.
ACCEPT ID-F-ACT.
OPEN EXTEND F-ACT.
DISPLAY "Bom trabalho!".
FINALIZACAO.
CLOSE F-ACT.
DISPLAY "Fim da sessão".
IDENTIFICATION DIVISION.
PROGRAM-ID. CRIACT.
AUTHOR. GUERREIRO.
26
• Condição de classe: NUMERIC, ALPHABETIC.
• Constantes figurativas: SPACE, SPACES.
27
4.1. Ordenação do ficheiro F-ACT
ENVIRONMENT DIVISION.
INPUT-OUTPUT SECTION.
FILE-CONTROL.
SELECT F-ACT ASSIGN TO ID-F-ACT.
SELECT F-ACT-ORD ASSIGN TO ID-F-ACT-SORT.
SELECT SORT-F ASSIGN TO ID-SORT-F.
DATA DIVISION.
WORKING-STORAGE SECTION.
77 ID-F-ACT PIC X(32).
77 ID-F-ACT-SORT PIC X(32).
77 ID-SORT-F PIC X(32).
FILE SECTION.
FD F-ACT.
01 ACT-REG PIC X(156).
FD F-ACT-ORD.
01 ACT-ORD-REG PIC X(156).
SD SORT-F.
01 SORT-F-REG.
02 FILLER PIC X.
02 S-F-CHAVE PIC 9(4).
02 FILLER PIC X(151).
28
A instrução que desencadeia a ordenação é a seguinte:
PROCEDURE DIVISION.
DISPLAY "Nome do ficheiro a ordenar: " NO ADVANCING.
ACCEPT ID-F-ACT.
DISPLAY "Nome do ficheiro ordenado: " NO ADVANCING.
ACCEPT ID-F-ACT-ORD.
SORT SORT-F ON ASCENDING KEY S-F-CHAVE
USING F-ACT
GIVING F-ACT-ORD.
DISPLAY "Ordenação concluída".
STOP RUN.
4.2. Recapitulação
29
A parte declarativa do programa é análoga à dos precedentes. Vejamos
então a algoritmia:
Programa
Inicialização Finalização
eof-act-ord
fim-
Início-grupo Avançar grupo
Funcs-uni Funcs-dup
77 B-EOF-ACT-ORD PIC 9.
88 EOF-ACT-ORD VALUE 1.
30
memorizado ao ficheiro F–ACT-UNI), ou um tratamento iterativo de recolha
de todos os registos com a mesma chave para o ficheiro F–ACT–DUP.
77 B-EOF-X PIC 9.
88 EOF-X VALUE '1'.
CADA-GRUPO.
MOVE '0' TO B-FIM-GRUPO.
MOVE ACT-ORD-REG TO TAMPAO.
PERFORM AVANCAR-ACT-ORD.
IF FIM-GRUPO PERFORM GRUPO-UNI
ELSE PERFORM GRUPO-DUP.
é equivalente a:
31
READ F-X. MOVE X-REG TO Z.
AVANCAR-ACT-ORD.
READ F-ACT-ORD AT END MOVE '1' TO B-EOF-ACT-ORD.
IF EOF-ACT-ORD MOVE '1' TO B-FIM-GRUPO
ELSE IF ACT-ORD-NUM-FUNC NOT = TAMPAO-NUM-FUNC
MOVE '1' TO B-FIM-GRUPO.
GRUPO-UNI.
WRITE ACT-UNI-REG FROM TAMPAO.
Aqui está uma instrução de escrita! Tal como os READs, os WRITEs têm
em COBOL muitas variantes. Para já, chegam-nos duas, uma que correspon-
de ao write do Pascal, e outra ao put. A forma WRITE FROM, agora utilizada,
é a que está no primeiro caso. De facto, WRITE ACT-UNI-REG FROM TAMPAO
escrever-se-ia em Pascal write(f_act_uni, tampao). Em geral, dado um
ficheiro F-Y em escrita, com registo Y-REG, e uma variável (ou constante) Z,
podemos considerar que:
é equivalente a:
write(f_y, z)
WRITE Y-REG.
corresponde a:
put(f_y)
32
pode ser visto como uma abreviatura de:
GRUPO-DUP.
WRITE ACT-DUP-REG FROM TAMPAO.
PERFORM MAIS-UM UNTIL FIM-GRUPO.
MAIS-UM.
WRITE ACT-DUP-REG FROM ACT-ORD-REG.
PERFORM AVANCAR-ACT-ORD.
Já está quase tudo. Mas o enunciado pede também para contar o nú-
mero de chaves que aparecem repetidas. Para contar, é preciso um contador,
isto é, uma variável inteira, inicializada a zero (no procedimento de inicializa-
ção, que trata das inicializações globais), e incrementada de cada vez que se
detecta um grupo múltiplo. O lugar ideal para colocar esta operação é à en-
trada de GRUPO–DUP. Vejamos primeiro a forma da declaração da variável:
WORKING-STORAGE SECTION.
...
77 N-DUPS PIC 9(4) USAGE COMP.
Esta complicação toda é para significar que N-DUPS é uma variável in-
teira (como as do Pascal, por exemplo), e não uma cadeia de algarismos (aten-
ção que o PIC 9(4) esclarece, neste contexto, que a representação binária do
inteiro ocupa quatro bytes, e não que os valores possíveis vão de 0 a 9999).
ADD 1 TO N-DUPS.
33
ADD N TO R.
SUBTRACT N FROM R.
MULTIPLY N BY R.
DIVIDE N INTO R.
Note-se que N deve ser uma variável ou uma constante, e não pode ser
uma expressão arbitrária. No caso da divisão de um inteiro por outro, o resul-
tado é o quociente da divisão inteira. Quando se quiser o resto, tem que usar-
-se o seguinte esquema:
Aqui está um caso em que o obsoleto COBOL bate muitos dos seus con-
correntes mais recentes!
COMPUTE R = exp
INICIALIZACAO.
DISPLAY "Nome do ficheiro de entrada? " NO ADVANCING.
ACCEPT ID-F-ACT-ORD.
DISPLAY "Nome do ficheiro verificado? " NO ADVANCING.
ACCEPT ID-F-ACT-UNI.
DISPLAY "Nome do ficheiro dos duplicados? " NO ADVANCING.
ACCEPT ID-F-ACT-DUP.
OPEN INPUT F-ACT-ORD
OUTPUT F-ACT-UNI F-ACT-DUP.
MOVE 0 TO B-EOF-ACT-ORD.
READ F-ACT-ORD AT END MOVE 1 TO B-EOF-ACT-ORD.
MOVE O TO N-DUPS.
FINALIZACAO.
CLOSE F-ACT-ORD, F-ACT-UNI, F-ACT-DUP.
DISPLAY "Registos com chaves duplicadas: ", N-DUPS.
34
6. Actualização do ficheiro de identificação de pessoal
77 B-EOF-ACT PIC 9.
88 EOF-ACT VALUE '1'.
77 B-EOF-ID-V PIC 9.
88 EOF ID-V VALUE '1'.
35
Durante o processamento do ciclo inicial — chamemos-lhe caso normal — es-
tão disponíveis em cada momento dois registos, um para cada ficheiro de en-
trada. Se o número de funcionário em F–ACT–UNI for inferior ao de F–ID–V,
só pode tratar-se de uma criação; se os dois números de funcionário são
iguais, tem que ser uma remoção ou uma modificação; senão, não há actuali-
zação, e tudo o que há a fazer é copiar o registo de F–ID–V para F–ID–N. Es-
tas considerações conduzem-nos à seguinte árvore programática:
Programa
Inicialização Finalização
eof-id-v ou eof-act-uni eof-id-v
eof-act-uni
Caso Novos
Cópia
normal funcs
Criação Modif ou
Cópia
remoção
(Em rigor, esta árvore não é uma árvore: diversos procedimentos apa-
recem em sítios diferentes. Mas transformamo-la de novo numa árvore, de-
cretando que se trata de exemplares diferentes do mesmo procedimento…)
ENVIRONMENT DIVISION.
INPUT-OUTPUT SECTION.
FILE-CONTROL.
SELECT F-ID-V ASSIGN TO ID-F-ID-V.
SELECT F-ACT ASSIGN TO ID-F-ACT.
SELECT F-ID-N ASSIGN TO ID-F-ID-N.
SELECT F-CTRL ASSIGN TO ID-F-CTRL.
...
DATA DIVISION.
FILE SECTION.
FD F-ID-V.
01 ID-V-REG.
02 ID-V-NUM-FUNC PIC 9(4).
02 FILLER PIC X(151).
36
FD F-ACT.
01 ACT-REG.
02 ACT-TIPO-OP PIC A.
88 OP-CRIACAO VALUE "C".
88 OP-REMOCAO VALUE "R".
88 OP-MODIFICACOES VALUE "M".
02 ACT-INFO.
03 ACT-NUM-FUNC PIC 9(4).
03 ACT-NOME.
04 ACT-APELIDO PIC X(16).
04 ACT-RESTO-NOME PIC X(32).
03 ACT-BI PIC 9(8).
03 ACT-NO-CONTRIB PIC 9(9).
03 ACT-NASC.
04 ACT-NASC-ANO PIC 99.
04 ACT-NASC-MES PIC 99.
04 ACT-NASC-DIA PIC 99.
03 ACT-MORADA.
04 ACT-MORADA-1 PIC X(30).
04 ACT-MORADA-2 PIC X(30).
04 ACT-MORADA-3 PIC X(20).
FD F-ID-N.
01 ID-N-REG.
02 ID-N-NUM-FUNC PIC 9(4).
02 ID-N-NOME.
03 ID-N-APELIDO PIC X(16).
03 ID-N-RESTO-NOME PIC X(32).
02 ID-N-BI PIC 9(8).
02 ID-N-NO-CONTRIB PIC 9(9).
02 ID-N-NASC PIC X(6).
02 ID-N-MORADA PIC X(80).
FD F-CTRL.
01 CTRL-REG.
02 CTRL-ACT-REG.
03 CTRL-OP PIC A.
03 FILLER PIC X(4).
03 CTRL-APELIDO PIC X(16).
03 FILLER PIC X(135).
02 CTRL-STATUS PIC X.
GET-ID-V.
READ F-ID-V AT END MOVE 1 TO B-EOF-ID-V.
GET-ACT.
READ F-ACT AT END MOVE 1 TO B-EOF-ACT.
37
ESCR-CTRL.
WRITE CTRL-REG.
CASO-NORMAL.
IF ACT-NUM-FUNC < ID-V-NUM-FUNC PERFORM NOVOS-FUNCS
ELSE IF ACT-NUM-FUNC = ID-V-NUM-FUNC PERFORM MODIF-REM
ELSE PERFORM COPIA.
NOVOS-FUNCS.
IF OP-CRIACAO PERFORM CRIACAO
MOVE ZERO TO CTRL-STATUS
ELSE MOVE '1' TO CTRL-STATUS.
MOVE ACT-REG TO CTRL-ACT-REG.
PERFORM ESCR-CTRL.
PERFORM GET-ACT.
MODIF-REM.
IF OP-REMOCAO PERFORM REMOCAO
MOVE ZERO TO CTRL-STATUS
ELSE IF OP-MODIFICACOES PERFORM MODIFICACOES
MOVE ZERO TO CTRL-STATUS
ELSE MOVE '1' TO CTRL-STATUS.
MOVE ACT-REG TO CTRL-ACT-REG.
IF CTRL-APELIDO = SPACES
MOVE ID-V-APELIDO TO CTRL-APELIDO.
PERFORM ESCR-CTRL.
PERFORM GET-ACT. PERFORM GET-ID-V.
COPIA.
WRITE ID-N-REG FROM ID-V-REG.
PERFORM GET-ID-V.
CRIACAO.
WRITE ID-N-REG FROM ACT-INFO.
REMOCAO.
38
MODIFICACAO.
MOVE ID-V-ACT TO ID-N-ACT.
IF ACT-APELIDO NOT = SPACES
MOVE ACT-APELIDO TO ID-N-APELIDO.
IF ACT-RESTO-NOME NOT = SPACES
MOVE ACT-RESTO-NOME TO ID-N-RESTO-NOME.
IF ACT-BI NOT = SPACES
MOVE ACT-BI TO ID-N-BI.
IF ACT-NO-CONTRIB NOT = SPACES
MOVE ACT-NO-CONTRIB TO ID-N-NO-CONTRIB.
IF ACT-NASC NOT = SPACES
MOVE ACT-NASC TO ID-N-NASC.
IF ACT-MORADA NOT = SPACES
MOVE ACT-MORADA TO ID-N-MORADA.
e, muito menos:
Cada endereço deve ser formado por cinco linhas: a primeira é constan-
te, contendo a cadeia “EXMO(A) SENHOR(A)”; a segunda deve exibir o nome
do funcionário, escrito como habitualmente, isto é com o apelido no fim; as
três restantes limitam-se a reproduzir as três linhas do campo morada nos re-
gistos de identificação. Cada endereço aparece numa página diferente. (A im-
pressora deverá ser programada para compatibilizar o comprimento das pági-
nas com o tamanho das etiquetas.)
39
para achar o último carácter diferente de espaço no resto do nome, e depois
para escrever os caracteres significativos.
WORKING-STORAGE SECTION.
01 CADEIA.
02 CARS PIC X OCCURS 80.
01 CADEIA.
02 CARS PIC X OCCURS 80 INDEXED BY I-CARS.
Isto não é obrigatório, mas como quase sempre que se tem um vector se
quer fazer algum tipo de iteração sobre os seus elementos, o esquema até é
bastante simpático. Além disso, o funcionamento da instrução SEARCH, de
busca em vectores, baseia-se num ciclo implícito, controlado pelo índice asso-
ciado ao vector, que nesse caso não pode ser omitido.
WORKING-STORAGE SECTION.
...
77 N-CARS PIC 9(4) USAGE COMP.
CALC-COMPRIMENTO.
MOVE 0 TO I-CARS.
SEARCH CARS AT END MOVE ZERO TO N-CARS
WHEN CARS (80 - I-CARS) NOT = SPACE
COMPUTE N-CARS = 80 - I-CARS.
40
Palavras para quê?
01 CADEIA-2.
02 CARS-2 PIC X OCCURS 80 INDEXED BY I-CARS-2.
...
77 N-CARS-2 PIC 9(4) USAGE COMP.
Claro que não vale a pena duplicar também o parágrafo que serve para
calcular o comprimento. Essa operação poderá em regra ser feita colocando a
cadeia na variável CADEIA e indo recolher o resultado a N-CARS. Podemos
ainda decidir que, ao fazer a concatenação, seria má programação perder tem-
po a acrescentar os espaços não significativos do apelido, e que, por isso, é
preciso começar por determinar o seu comprimento:
CONSTRUCAO-NOME.
MOVE ID-APELIDO TO CADEIA, CADEIA-2.
PERFORM CALC-COMPRIMENTO.
MOVE N-CARS TO N-CARS-2.
MOVE ID-RESTO-NOME TO CADEIA.
PERFORM CALC-COMPRIMENTO.
COMPUTE I-CARS = N-CARS + 1.
PERFORM JUNTAR-LETRA VARYING I-CARS-2 FROM 1 BY 1
UNTIL I-CARS-2 > N-CARS-2.
JUNTA-LETRA.
ADD 1 TO I-CARS.
MOVE CARS-2 (I-CARS-2) TO CARS (I-CARS).
41
Programa
Inicialização Finalização
eof-id
uma
etiqueta
ENVIRONMENT DIVISION.
INPUT-OUTPUT SECTION.
SELECT F-ID ASSIGN TO ID-F-ID.
SELECT F-ETIQ ASSIGN TO PRINTER ID-F-ETIQ.
Cada “registo” deste ficheiro F-ETIQ é uma linha. Não tem qualquer
estrutura, para além de ser formado por uma sequência de caracteres. A li-
nha mais comprida é a do nome, que pode ter até 49 caracteres. Por isso, bas-
ta declarar as coisas assim:
DATA DIVISION.
FILE SECTION.
FD F-ETIQ.
01 ETIQ-REG PIC X(49).
UMA-ETIQUETA.
PERFORM CONSTRUCAO-NOME.
PERFORM ESCR-ETIQ.
READ F-ID AT END MOVE 1 TO B-EOF-ID.
42
ESCR-ETIQ.
WRITE ETIQ-REG FROM "EXMO(A) SENHOR(A)" AFTER PAGE.
WRITE ETIQ-REG FROM CADEIA.
WRITE ETIQ-REG FROM ID-MORADA-1.
WRITE ETIQ-REG FROM ID-MORADA-2.
WRITE ETIQ-REG FROM ID-MORADA-3.
43