16. Práticas e recursos a evitar
Toda linguagem de programação oferece recursos que, se mal utilizados, podem comprometer a qualidade do código e a facilidade de manutenções futuras. E isso é mais verdade ainda numa linguagem como o COBOL que existe há décadas e está em constante evolução.
Alguns recursos que eram úteis nos anos 1960 e 1970 para superar limitações de hardware e software não são mais necessários nos dias de hoje. Mas a preocupação do COBOL em garantir compatibilidade total com as versões anteriores faz com que esses recursos estejam disponíveis até hoje na maioria dos compiladores. O COBOL é uma das poucas linguagens de programação que não têm comandos “deprecados”.
Alguns desses recursos podem ser encontrados em antigos programas que ainda estão funcionando plenamente em sistemas legados. Por esse motivo é importante que você conheça alguns comandos, estruturas, abordagens e técnicas de programação que ainda são suportados pela linguagem mas que você deve evitar nos seus programas novos e/ou tentar substituir nos programas antigos, quando for possível.
O controverso GO TO
O comando GO TO é provavelmente um dos mais combatidos desde que surgiram as primeiras linguagens programação de terceira geração. O senso comum alega que seu uso prejudica a leitura do programa ao forçar desvios incondicionais de qualquer lugar para qualquer lugar sem restrições.
Mais uma vez, o problema está na dose do remédio, que pode matar ou curar. Naturalmente, você nunca deverá codificar um programa que faça desvios para frente ou para trás indiscriminadamente.
O primeiro programa de exemplo que construímos nesse livro usava esse comando. Era um programa bem simples, onde o GO TO foi usado para provocar um loop de leitura do arquivo e depois para sair desse loop. Todos os exemplos posteriores passaram a usar a técnica de programação estruturada, e talvez você tenha percebido que nenhum desvio foi necessário.
Mas existe uma situação onde o GO TO seria aceitável e não comprometeria o entendimento do programa. Essa situação ocorre quando precisamos interromper o fluxo de execução de um parágrafo, como se fosse um comando break da linguagem C ou Java.
Observe o parágrafo abaixo, extraído de um programa real. Esse parágrafo tem por objetivo validar um arquivo de chamadas telefônicas. Ele foi modificado para mostrar que, tecnicamente, seria possível construí-lo sem o uso de desvios. Também retiramos as linhas de comentários para deixar o trecho mais curto:
PERFORM S005-PROCESSAMENTO ... S005-PROCESSAMENTO. MOVE ZEROS TO W31-CODG-ERRO-AUX W31-CODG-SERVICO-AUX W32-CODG-NAT COMPUTE W32-CODG-NAT = W01-NATUREZA + 1 IF W22-CODG-NATUREZA(W32-CODG-NAT) = ZEROS DISPLAY 'NATUREZA NAO CADASTRADA ' W01-NATUREZA MOVE 51630 TO W31-CODG-ERRO-AUX ELSE IF W22-INFO-FATURA(W32-CODG-NAT) = 'N' MOVE 51630 TO W31-CODG-ERRO-AUX ELSE IF W01-EOT-ORIGEM = ZEROS DISPLAY 'EOT ORIGEM ZERADA ' MOVE 51651 TO W31-CODG-ERRO-AUX ELSE IF W21-COD-EOT(W01-EOT-ORIGEM) = ZEROS DISPLAY 'EOT ORIGEM NAO CADASTRADA ' W01-EOT-ORIGEM MOVE 51651 TO W31-CODG-ERRO-AUX ELSE IF W01-IDENT-REGISTRO = '1' AND W01-EOT-DESTINO = ZEROS DISPLAY 'EOT DESTINO ZERADA ' MOVE 51652 TO W31-CODG-ERRO-AUX ELSE IF W01-IDENT-REGISTRO = ‘1’ AND W21-COD-EOT(W01-EOT-DESTINO) = ZEROS DISPLAY 'EOT DESTINO NAO CADASTRADA ' W01-EOT-DESTINO MOVE 51652 TO W31-CODG-ERRO-AUX ELSE IF W01-EOT-DESTINO NOT = ZEROS DISPLAY 'EOT DESTINO NAO ZERADA ' W01-EOT-DESTINO MOVE 51652 TO W31-CODG-ERRO-AUX ELSE IF W01-CSP NOT = W31-CSP-AUX DISPLAY 'CSP INVALIDO ' W01-CSP MOVE 51631 TO W31-CODG-ERRO-AUX ELSE IF W01-CNL-ORIGEM = ZEROS DISPLAY 'CNL ORIGEM ZERADA ' MOVE 51666 TO W31-CODG-ERRO-AUX ELSE IF W19-NUMR-CNL(W01-CNL-ORIGEM) = ZEROS DISPLAY 'CNL ORIGEM NAO CADASTRADA ' W01-CNL-ORIGEM MOVE 51666 TO W31-CODG-ERRO-AUX ELSE IF 88TIPO-ARQ-SMP AND W01-CODG-DDD NOT EQUAL W01-CNL-ORIGEM DISPLAY 'CNL ORIGEM DIFERENTE DDD ' W01-CNL-ORIGEM MOVE 51666 TO W31-CODG-ERRO-AUX ELSE IF W01-IDENT-REGISTRO EQUAL '1' AND W01-CNL-DESTINO EQUAL ZEROS DISPLAY 'CNL DESTINO ZERADA ' MOVE 51668 TO W31-CODG-ERRO-AUX ELSE IF W01-IDENT-REGISTRO EQUAL ‘1’ AND W19-NUMR-CNL(W01-CNL-DESTINO) EQUAL 0 DISPLAY 'CNL DESTINO NAO CADASTRADA ' W01-CNL-DESTINO MOVE 51668 TO W31-CODG-ERRO-AUX ELSE IF W01-IDENT-REGISTRO EQUAL ‘1’ AND W01-CNL-DESTINO EQUAL ZEROS DISPLAY 'CODIGO PAIS NAO CADASTRADO' MOVE 51637 TO W31-CODG-ERRO-AUX ELSE IF W01-GRUPO-HORARIO GREATER 5 DISPLAY 'GRUPO HORARIO INVALIDO ' W01-GRUPO-HORARIO MOVE 51645 TO W31-CODG-ERRO-AUX ELSE IF W01-IDENT-REGISTRO EQUAL '1' AND W01-DEGRAU-N NOT EQUAL 99 AND W20-EXISTE-DEGRAU(W01-DEGRAU-N) NOT EQUAL 'S' DISPLAY 'DEGRAU INVALIDO ' W01-DEGRAU MOVE 51671 TO W31-CODG-ERRO-AUX ELSE IF NOT (W01-COD-CONTEST EQUAL ' ' OR = 'RC' OR = 'RR' OR = 'RD' OR = 'RP') DISPLAY 'COD CONTESTACAO INVALIDA ' W01-COD-CONTEST MOVE 51648 TO W31-CODG-ERRO-AUX END-IF END-IF END-IF END-IF END-IF END-IF END-IF END-IF END-IF END-IF END-IF END-IF END-IF END-IF END-IF END-IF END-IF.
Ok, os dezessete END-IFs no final do parágrafo foi um exagero. Bastaria ter encerrado o último IF com um ponto:
ELSE IF NOT (W01-COD-CONTEST EQUAL ' ' OR = 'RC' OR = 'RR' OR = 'RD' OR = 'RP') DISPLAY 'COD CONTESTACAO INVALIDA ' W01-COD-CONTEST MOVE 51648 TO W31-CODG-ERRO-AUX.
Repare que tivemos que mudar a edentação do programa. Se fizéssemos aquela edentação padrão, deslocando o próximo IF quatro bytes para a direita depois de cada ELSE, certamente faltaria colunas no final do parágrafo. Afinal, uma linha de programa COBOL só pode ir até a coluna 72.
Mas seria aceitável criar um ponto de saída (um parágrafo chamado, digamos, S005-FIM, e modificar o PERFORM incluindo opção THRU.
PERFORM S005-PROCESSAMENTO THRU S005-FIM
Essa opção faz com que o comando execute mais de um parágrafo. Ele executará do primeiro comando do parágrafo S005-PROCESSAMENTO até o último comando do parágrafo S005-FIM, incluindo (se for o caso) quaisquer outros parágrafos que existam entre esses dois.
No nosso exemplo, não haveria nenhum outro parágrafo entre o parágrafo inicial e o final. E o parágrafo final teria um único comando, EXIT, que não tem nenhuma outra função além de marcar esse parágrafo como ponto de saída.
Isso nos permitiria quebrar a cadeia de IFs e incluir um comando que desviasse para S005-FIM sempre que as condições fossem verdadeiras. Observe como era o parágrafo originalmente e faça sua avaliação:
PERFORM S005-PROCESSAMENTO THRU S005-FIM ... S005-PROCESSAMENTO. MOVE ZEROS TO W31-CODG-ERRO-AUX W31-CODG-SERVICO-AUX W32-CODG-NAT COMPUTE W32-CODG-NAT = W01-NATUREZA + 1 IF W22-CODG-NATUREZA(W32-CODG-NAT) = ZEROS DISPLAY 'NATUREZA NAO CADASTRADA ' W01-NATUREZA MOVE 51630 TO W31-CODG-ERRO-AUX GO TO S005-FIM END-IF IF W22-INFO-FATURA(W32-CODG-NAT) = 'N' MOVE 51630 TO W31-CODG-ERRO-AUX GO TO S005-FIM END-IF IF W01-EOT-ORIGEM = ZEROS DISPLAY 'EOT ORIGEM ZERADA ' MOVE 51651 TO W31-CODG-ERRO-AUX GO TO S005-FIM END-IF IF W21-COD-EOT(W01-EOT-ORIGEM) = ZEROS DISPLAY 'EOT ORIGEM NAO CADASTRADA ' W01-EOT-ORIGEM MOVE 51651 TO W31-CODG-ERRO-AUX GO TO S005-FIM END-IF IF W01-IDENT-REGISTRO = '1' IF W01-EOT-DESTINO = ZEROS DISPLAY 'EOT DESTINO ZERADA ' MOVE 51652 TO W31-CODG-ERRO-AUX GO TO S005-FIM END-IF IF W21-COD-EOT ( W01-EOT-DESTINO ) = ZEROS DISPLAY 'EOT DESTINO NAO CADASTRADA ' W01-EOT-DESTINO MOVE 51652 TO W31-CODG-ERRO-AUX GO TO S005-FIM END-IF ELSE IF W01-EOT-DESTINO NOT = ZEROS DISPLAY 'EOT DESTINO NAO ZERADA ' W01-EOT-DESTINO MOVE 51652 TO W31-CODG-ERRO-AUX GO TO S005-FIM END-IF END-IF IF W01-CSP NOT = W31-CSP-AUX DISPLAY 'CSP INVALIDO ' W01-CSP MOVE 51631 TO W31-CODG-ERRO-AUX GO TO S005-FIM END-IF IF W01-CNL-ORIGEM = ZEROS DISPLAY 'CNL ORIGEM ZERADA ' MOVE 51666 TO W31-CODG-ERRO-AUX GO TO S005-FIM END-IF IF W19-NUMR-CNL(W01-CNL-ORIGEM) = ZEROS DISPLAY 'CNL ORIGEM NAO CADASTRADA ' W01-CNL-ORIGEM MOVE 51666 TO W31-CODG-ERRO-AUX GO TO S005-FIM END-IF IF 88TIPO-ARQ-SMP IF W01-CODG-DDD NOT EQUAL W01-CNL-ORIGEM DISPLAY 'CNL ORIGEM DIFERENTE DDD ' W01-CNL-ORIGEM MOVE 51666 TO W31-CODG-ERRO-AUX GO TO S005-FIM END-IF END-IF IF W01-IDENT-REGISTRO EQUAL '1' IF W01-CNL-DESTINO EQUAL ZEROS DISPLAY 'CNL DESTINO ZERADA ' MOVE 51668 TO W31-CODG-ERRO-AUX GO TO S005-FIM END-IF IF W19-NUMR-CNL(W01-CNL-DESTINO) EQUAL 0 DISPLAY 'CNL DESTINO NAO CADASTRADA ' W01-CNL-DESTINO MOVE 51668 TO W31-CODG-ERRO-AUX GO TO S005-FIM END-IF ELSE IF W01-CNL-DESTINO EQUAL ZEROS DISPLAY 'CODIGO PAIS NAO CADASTRADO' MOVE 51637 TO W31-CODG-ERRO-AUX GO TO S005-FIM END-IF END-IF IF W01-GRUPO-HORARIO GREATER 5 DISPLAY 'GRUPO HORARIO INVALIDO ' W01-GRUPO-HORARIO MOVE 51645 TO W31-CODG-ERRO-AUX GO TO S005-FIM END-IF IF W01-IDENT-REGISTRO EQUAL '1' IF W01-DEGRAU-N NOT EQUAL 99 IF W20-EXISTE-DEGRAU(W01-DEGRAU-N) NOT EQUAL 'S' DISPLAY 'DEGRAU INVALIDO ' W01-DEGRAU MOVE 51671 TO W31-CODG-ERRO-AUX GO TO S005-FIM END-IF END-IF END-IF IF NOT (W01-COD-CONTEST EQUAL ' ' OR = 'RC' OR = 'RR' OR = 'RD' OR = 'RP') DISPLAY 'COD CONTESTACAO INVALIDA ' W01-COD-CONTEST MOVE 51648 TO W31-CODG-ERRO-AUX GO TO S005-FIM END-IF. S005-FIM. EXIT.
Muitas empresas adotam esse padrão. Há algumas, inclusive que exigem o uso do PERFORM THRU para todos os parágrafos, mesmo quando eles não precisam de desvios para o final. Outras empresas proíbem seu uso terminantemente. Há autores que consideram uma opção válida, outros que rejeitam completamente.
O fato é que, se realmente essa técnica for usada, é importante que ela faça parte de um padrão de codificação. Por exemplo, o nome do parágrafo final pode ser formado pelo prefixo do parágrafo inicial mais a palavra “FIM”, como fizemos nesse exemplo. Os GO TOs serão sempre para o parágrafo final, nunca para outro lugar.
Como não há questões relacionadas a consumo ou performance, a pergunta que devemos sempre fazer é: essa abordagem prejudica suas manutenções posteriores? Outro programador, anos depois, terá dificuldade para entender e alterar o código? Se a resposta for sim, devemos evitá-la; se for não, podemos prosseguir.
O inútil GO TO DEPENDING ON
Pior do que um GO TO é um GO TO que depende do valor de uma variável, em tempo de execução, para decidir para onde vai. E é justamente essa a função do comando GO TO DEPENDING ON.
Alguns autores chamam esse comando de GO TO condicional, uma vez que ele exige uma decisão do programa. Seu formato geral é:
GO TO Paragrafo1 Paragrafo2 Paragrafo3 ... ParagrafoN DEPENDING ON variável
Ele provoca um desvio para um dos parágrafos indicados dependendo do valor numérico da variável informada após a cláusula DEPENDING ON. Se o valor dessa variável for 1, dele desvia para o primeiro parágrafo informado; se for 2, ele desvia para o segundo; e assim sucessivamente.
A variável deve ter um valor consistente com a quantidade de parágrafos codificados no comando. Se esse valor for negativo, se for zero ou se for maior que a quantidade de parágrafos indicados, o comando simplesmente é ignorado.
O trecho abaixo mostra o uso desse comando na prática:
PROCEDURE DIVISION.
INICIO.
OPEN INPUT MOVIMENTO
I-O CADASTRO.
PROCESSA.
READ MOVIMENTO AT END GO TO FIM.
GO TO INCLUI-REGISTRO
ALTERA-REGISTRO
EXCLUI-REGISTRO
DEPENDING ON MOVIMENTO-OPERACAO.
GO TO PROCESSA.
INCLUI-REGISTRO.
MOVE MOVIMENTO-REGISTRO TO CADASTRO-MOVIMENTO
WRITE CADASTRO-MOVIMENTO
GO TO PROCESSA.
ALTERA-REGISTRO.
MOVE MOVIMENTO-REGISTRO TO CADASTRO-REGISTRO
REWRITE CADASTRO-REGISTRO
GO TO PROCESSA.
EXCLUI-REGISTRO.
MOVE MOVIMENTO-CHAVE TO CADASTRO-CHAVE
DELETE CADASTRO
GO TO PROCESSA.
FIM.
CLOSE MOVIMENTO CADASTRO
STOP RUN.
Nesse exemplo, o campo MOVIMENTO-OPERACAO, que faz parte do arquivo de entrada, é um campo numérico que pode estar com os valores 1, 2 ou 3. Esse valor fará com que o GO TO DEPENDING ON desvie, respectivamente, para os parágrafos INCLUI-REGISTRO, ALTERA-REGISTRO ou EXCLUI-REGISTRO. Se esse campo estiver preenchido com qualquer valor diferente de 1, 2 ou 3, o comando será ignorado e o fluxo de execução será desviado para nova leitura.
O GO TO DEPENDING ON exige técnicas não-estruturadas de programação, o que pode prejudicar significativamente a clareza do programa. Ele foi muito usado antes da revisão do COBOL de 1985, que implementou estruturas case. O surgimento do comando EVALUATE, nessa versão, tornou o GO TO DEPENDING ON obsoleto e desnecessário.
O famigerado ALTER
Esse talvez seja a única unanimidade quando se discute práticas de programação em COBOL. Poucos comandos podem ser tão contrários às boas práticas quanto o comando ALTER.
Esse comando existe desde a primeira versão do COBOL, de 1959. A ideia era permitir que o programa fosse “automodificável”. E essa expressão, por si só, já dá uma ideia dos efeitos negativos que ele pode provocar na facilidade de manutenções futuras.
O ALTER permite que, em tempo de execução, um comando GO TO seja modificado para desviar para um outro parágrafo, que não aquele que foi originalmente codificado pelo programador. Sua sintaxe é:
ALTER parágrafo1 TO PROCEED TO parágrafo2
A natureza desse comando, por si só, encoraja a utilização de técnicas não-estruturadas de programação. Para entendê-lo é mais fácil acompanhar um exemplo hipotético:
PROCEDURE DIVISION. INICIO. ACCEPT PARAMETRO FROM COMMAND-LINE IF PARAMETRO = “A” ALTER PROCESSAMENTO TO PROCEED TO SOMA-VALORES ELSE ALTER PROCESSAMENTO TO PROCEED TO SUBTRAI-VALORES END-IF OPEN INPUT MOVIMENTO OPEN I-O CADASTRO. LE-MOVIMENTO. READ MOVIMENTO AT END GO TO FIM. MOVE MOVIMENTO-CHAVE TO CADASTRO-CHAVE. READ CADASTRO INVALID KEY GO TO LE-MOVIMENTO. PROCESSAMENTO. GO TO SOMA-VALORES. SOMA-VALORES. ADD MOVIMENTO-VALOR TO CADASTRO-VALOR REWRITE CADASTRO-REGISTRO GO TO LE-MOVIMENTO. SUBSTRAI-VALORES. SUBTRACT MOVIMENTO-VALOR FROM CADASTRO-VALOR REWRITE CADASTRO-REGISTRO GO TO LE-MOVIMENTO. FINALIZA. CLOSE MOVIMENTO CADASTRO STOP RUN.
Esse programa recebe um argumento (chamado PARAMETRO) que pode ser “A” ou “B”. Se o valor for “A” ele vai somar os valores do arquivo MOVIMENTO aos valores do arquivo CADASTRO. Se o valor for “B” ele vai subtrair esses valores. As somas e subtrações acontecem em dois parágrafos distintos, e logo após a leitura do argumento o programa decide para que parágrafo o fluxo será desviado durante a sua execução.
Uma exigência da sintaxe do ALTER é que o GO TO seja o único comando do parágrafo afetado.
Observe que após o IF inicial, o parágrafo PROCESSAMENTO poderá desviar para SUBTRAI-VALORES, apesar de originalmente ter sido codificado GO TO SOMA-VALORES. O programa fonte mostra uma coisa, mas em tempo de execução ele pode fazer outra.
Para entender a finalidade de um comando assim precisamos voltar no tempo e pensar nas restrições de hardware e software que existiam nos primeiros anos do COBOL. IFs eram recursos “caros”, pois geravam uma grande quantidade de instruções de máquina. Numa época em que os ciclos do processador eram lentos, economizar IFs podia fazer a diferença entre uma performance razoável e um desempenho inaceitável.
Nosso programa exemplo poderia ter executado um IF para cada registro lido, mas o ALTER no início do programa fez com que todo o fluxo fosse desviado uma única vez; o programa se “automodificou”.
Hoje em dia, não existe mais justificativa técnica para isso. Felizmente, o uso do comando ALTER foi limitado mesmo nos primeiros anos do COBOL. Dificilmente você encontrará um programa real que faça uso desse recurso.
O transtorno das SECTIONS
A PROCEDURE DIVISION pode ser dividida em SECTIONS, que por sua vez são divididas em parágrafos:
Uma seção termina quando começa a próxima seção, ou quando o programa termina. Seções recebem nomes, como os parágrafos. O que diferencia uma seção de um parágrafo é a palavra SECTION codificada depois do nome:
PROCEDURE DIVISION. S0-PRINCIPAL SECTION. PERFORM S1-PROCEDIMENTOS-INICIAIS ... S1-PROCEDIMENTOS-INICIAIS SECTION. P11-ABRE-ARQUIVOS. OPEN INPUT CRA0101 CRA0102 OUTPUT CRA0103. P12-LE-ARQUIVOS. READ CRA0101. READ CRA0102.
No trecho acima o comando PERFORM executa S1-PROCEDIMENTOS-INICIAIS, que sabemos que é uma seção por causa da palavra SECTION depois do seu nome.
Quando uma seção é executada, todos os parágrafos dessa seção são executados. No exemplo acima, o comando PERFORM é equivalente a:
PERFORM P11-ABRE-ARQUIVOS THRU P12-LE-ARQUIVOS
A lógica com seções pode ficar mais confusa, uma vez que não dá pra ler o PERFORM e saber se o que está sendo chamado é uma seção com vários parágrafos, ou apenas parágrafo.
A divisão da PROCEDURE em seções se justificava numa época em que memória era um recurso escasso. Os antigos compiladores conseguiam segmentar o programa de maneira que as seções fossem carregadas em memória uma de cada vez. Um programa muito grande, portanto, podia ser modularizado de tal forma que parágrafos muito dependentes uns dos outros seriam carregados juntos, o que melhorava significativamente a performance.
Hoje, o uso de seções tem pouquíssimos efeitos práticos sobre o desempenho do programa, com o prejuízo de deixar a lógica desnecessariamente mais complexa.
As feias DECLARATIVES
A PROCEDURE DIVISION permite que você crie seções especiais para capturar erros de I/O em operações com arquivos. Essas seções devem ser codificadas no início da PROCEDURE entre as palavras DECLARATIVES e END-DECLARATIVES, e seu uso exige que todo o resto do programa seja dividido em seções:
PROCEDURE DIVISION. DECLARATIVES. CAPTURA-ERRO-CRA0101 SECTION. USE AFTER ERROR PROCEDURE ON CRA0101 DISPLAY “ERRO “ WT-ST-CRA0101 “ NO ARQUIVO CRA0101”. CAPTURA-ERRO-CRA0102 SECTION. USE AFTER ERROR PROCEDURE ON CRA0102 DISPLAY “ERRO “ WT-ST-CRA0102 “ NO ARQUIVO CRA0102”. END-DECLARATIVES. INICIO-DO-PROGRAMA SECTION. ...
O comando USE estabelece em que condições essas seções serão executadas. No exemplo acima, a seção CAPTURA-ERRO-CRA0101 será executada sempre que ocorrer um erro de I/O no acesso ao arquivo CRA0101. Depois que o último comando da seção for executado, o fluxo de execução retorna para o comando seguinte àquele que provocou o erro de I/O.
O uso de DECLARATIVES dá uma falsa impressão de que você vai economizar linhas de código ao concentrar o tratamento de exceções num só lugar. No entanto, suas disfunções são várias. Se você deixar que a DECLARATIVE capture todo e qualquer erro de I/O que acontecer no programa, seu controle será menor sobre o que fazer em cada caso:
- Um erro na abertura do arquivo teria que ser tratado da mesma forma que um erro na leitura ou na gravação;
- Se você usar cláusulas como AT END ou INVALID KEY nas leituras, eventuais exceções serão tratadas por essas cláusulas, e não pelas DECLARATIVES, e você terá diferentes tratamentos de erro, acontecendo em diferentes lugares do programa;
Além disso, o uso de DECLARATIVES fere os princípios da programação estruturada. Quando um erro exigir o fim anormal do programa você terá que encerrá-lo nessas seções especiais, e seu programa passará a ter mais de um ponto de saída.
Testar o file status após cada comando OPEN, READ, WRITE, REWRITE, DELETE ou CLOSE é uma opção muito melhor. Além de oferecer mais liberdade sobre o que fazer em cada caso, você não prejudica a estruturação do programa.
Anterior | Conteúdo | Próxima |