ICC Turma D - UnB 2/04

Prática 8

1- Programas com vários ifs


Exercício 9.1

Faça um programa que recebe como entrada de dados o valor da renda anual ( r ) de um contribuinte da Receita Federal e o valor de imposto recolhido na fonte ( rf ) por este contribuinte (parcela paga antecipadamente via desconto na folha de pagamento), e produz como resultado o imposto devido ou a receber ( i ), conforme a fórmula: i = r*a/100  - d - rf e os dados da seguinte tabela progressiva.

Tabela progressiva para cálculo do imposto de renda
faixa de renda anual r
 alíquota a  (porcentagem)
para cálculo imposto
Parcela d a deduzir (da alíquota
aplicada sobre a renda)
até 12696.00
zero por cento
zero
acima de 12696.00 e até 25380.00
15%
1904.00
acima de 25380.00
27.5%
5076.90


Se o resultado for negativo, a saída do programa deve incluir o literal  "Imposto a receber: " seguido do valor do imposto a receber (valor de i sem o sinal de menos).  Se o resultado for positivo, a saída do programa deve incluir o literal  "Imposto a pagar: " seguido do valor do imposto que o contribuinte deve à Receita Federal (o valor de i ),



2- Usando contadores

Em jargão de programação, um contador é uma variável onde se armazena um valor numérico (normalmente um inteiro não negativo) cujo significado para o programa venha a ser o número de vezes que "alguma coisa" tenha se repetido durante a execução, ou durante sucessivas execuções do programa. Podemos imaginar, como exemplo de tal "coisa", a leitura de registros de um arquivo, o cálculo de uma expressão, a execução de um bloco de comandos, a ocorrência de algum tipo de evento com um objeto, etc.

Um exemplo prático está proposto no final da prática anterior. A prática 8 termina com a pergunta:
Como você poderia fazer o botão do formulário "se lembrar" de ..., por exemplo, quantas vezes já foi clicado? Para, por exemplo, informar esse dado ao usuário na sua própria legenda?
Usando o jargão acima, a resposta mais simples possível é: Com um contador.  Antes de praticarmos, porém, precisamos saber algo, um mínimo que seja, sobre o funcionamento de contadores em uma linguagem de programação imperativa, via scripts (como o javascript).

Preparando uma variável para funcionar como contador.

Para que uma variável funcione como contador, ela precisa aparecer, ao longo do código fonte, em pelo em duas atribuições com sintaxe e semântica específicas. Além, é claro, da expressão ou das expressões onde a informação nela contida vai ser usada. Essas duas atribuições são chamadas de inicialização e atualização. A sintaxe para essas atribuições é bastante simples:
Se, por exemplo, o contador for a variável n, a contagem começar em 1,  e proceder em direção crescente de um em um, teremos
...
n = 1;  // inicialização de contagem
...
   
...
    n = n + 1; // atualização de contagem progressiva
    ...
...
De tão frequente o seu uso, a atualização da contagem "de um em um" ganha, em muitas linguagens de programação -- incluindo o javascript -- uma abreviação:
_++   abrevia a atualização de contagem progressiva  _ = _ + 1
_--   abrevia a atualização  de contagem regressiva  _ = _ - 1
Assim, poderíamos substituir o exemplo acima, onde o contador seja a variável n, por
...
n = 1;  // inicialização de contagem
...
   
...
    n++; // atualização de contagem progressiva
    ...
...
Já a semântica exige um pouco mais de atenção. Observe as seguintes regras lógicas que precisam ser obedecidas para que a contagem funcione corretamente:
  1. A inicialização deve ser executada antes do trecho de código cujas repetidas execuções requerem contagem, e apenas uma vez.
  2. A atualização deve ser executada dentro do trecho de código cujas repetidas execuções requerem contagem, a cada repetição.
Além disso, observe que se o contador ocorrer apenas na inicialização e na atualização, ele não terá utilidade alguma. Se um contador conta repetições de algo, é porque esta contagem será útil em algum outro lugar do código-fonte. Que outro lugar seria esse, em relação ao comando de atualização, terá efeito no valor que se deve atribuir ao contador na inicialização.
Assim, no exemplo acima teríamos, na verdade, duas alternativas para uso de contador n, em contagem progressiva começando em 1.
...
n = 1;  // inicialização cont. progressiva começando em 1
...
   
...
    // comando(s) que usa(m) o contador n
    ...
    n++;  // atualização da contagem progressiva
    ...
...
ou então (observe a identação)
...
n = 0;  // inicialização cont. progressiva começando em 1
...
    ...
    n++;  // atualização da contagem progressiva
    ...
    // (zero ou mais) comando(s) que usa(m) o contador n
    ...
...
//
(zero ou mais) comando(s) que usa(m) o contador n
...
Depois de termos visto tudo isto, podemos voltar ao exemplo da prática 8, e ver como podemos programar o que foi sugerido. 

Gostariámos que o código contasse quantas vezes o botão foi clicado, para exibir esse dado na própria face do botão. Para isso, atualizamos um contador no próprio script do botão, para contar quantas vezes esse script teria executado. Se a única utilidade do botão for a de informar, em sua face, quantas vezes ele já foi clicado desde que a página foi carregada (o que não seria muito útil, mas serve de exemplo sobre como funcionam contadores), o código fonte correspondente poderia incluir o seguinte:
...
<form>
<input type="button" name="b" onClick="javascript:

    b.value = 'Esta é a ' + n + '-ésima vez que vc clica!';
    n = n + 1;

 
" value="clique aqui">
</form>

...

Um botão assim formado se comportaria como o botão abaixo.

Entretanto, se voce copiar o script acima ipse literis para uma casca de documento html, ou seja, se seu documento html for, literalmente,
<html>
<head><title>botao com memoria</title></head>
<body>
<form>
<input type="button" name="b" onClick="javascript:
    b.value = 'Esta é a ' + n + 'a. vez que vc clica!';
    n = n + 1;
 " value="clique aqui">
</form>
</body>
</html>
voce terá uma surpresa:  o seu botão não funcionará como esperado. Nem se mexe. Experimente, e depois responda: Por que o seu botão não funciona como o botão acima? Por que o primeiro comando nem executa? Digitando javascript: na caixa de localização do Mozilla e teclando "Enter" (ou clicando no botão amarelo no canto inferior esquerdo da moldura do Internet Explorer),  você terá a resposta: "a variável n, na linha 1 do script, não está definida"

Observe:

Se seu código-fonte estiver fora do estilo recomendado (notação identada), onde, por exemplo, mais de um comando ocupam a mesma linha de código fonte (neste caso, os dois únicos comandos), voce não saberá qual o comando estararia causando erro;
ao passo que, se tiver colocado um comando em cada linha, como acima, saberá que o erro ocorre no comando que atualiza o valor da face do botão b.

Apesar da notação identada ajudar a localizar a origem de erros, isso não é tudo. O ponto onde um erro é detectado indica apenas onde o interpretador "se perdeu", não necessariamente onde está o problema em seu código. Para vermos como, prossigamos com o exemplo. Havendo locailzado o comando com problema (variável n não declarada), voce poderia ser levado a pensar que, neste caso, o solução pode estar na ordem em que os comandos aparecem no script. Como o segundo comando conteria uma declaração implícita do contador n, bastaria trocar os comandos de posição no script. Tente trocá-los, e veja o que acontece quando o scipt passa a ser
<html>
<head><title>botao com memoria</title></head>
<body>
<form>
<input type="button" name="b" onClick="javascript:
    n = n + 1;
    b.value = 'Esta é a ' + n + 'a. vez que vc clica!';
  " value="clique aqui">
</form>
</body>
</html>
Voce verá então o mesmo comportamento do botão, que nem se mexe, e a mesma mensagem de erro. O erro agora estaria no comando de atualização do contador!

Por que a mensagem de erro do interpretador javascript diz, neste caso, que a variável n não está definida neste comando? Se, no exemplo acima, a variável n aparece pela primeira vez no lado esquerdo da atribuição, insto não constituiria uma declaração implícita de n, definindo n como variável? A resposta seria sim, mas desde que a expressão do lado direito seja válida.

O problema no script acima é que n também aparece, nesse mesmo comando de atribuição, do lado direito.  E isso tem que acontecer porque, numa atualização, o novo valor da contagem é estabelecido pela soma entre o valor anterior e o passo da contagem. Ou seja, numa atualização, a variável do lado esquerdo têm que aparecer também do lado direito. Ou então em um comando anterior, o que redundaria no mesmo tipo de erro.

Para entendermos o que está acontecendo, precisamos examinar mais de perto o comando de atribuição. Quando um comando de atribuição é executado, a primeira coisa que acontece é a avaliação da expressão do lado direito. Para que o resultado seja, depois, armazenado no objeto cujo nome aparece do lado esquerdo.  Podemos dizer, portanto, que o comando de atribuição é executado na direção oposta em que humanos o lêem. Quando o interpretador javascipt tenta avaliar a expressão n + 1 no lado direito do comando em exame, o interpretador não tem como saber qual valor estaria armazenado em n, já que ele ainda desconhece, no ambiente de execução deste script, objeto com tal nome.

 Tudo isto serve para dizer que a inicialização de contadores é imprescindivel.

Falta, no script acima, portanto e obviamente, a inicialização do contador n.  Assim, se voce tiver simplesmente copiado um dos códigos acima para seu documento html,  ao executar o script pela primeira vez clicando no botão o primeiro comando falhará, qualquer que seja a ordem em que estiverem dispostos os dois comandos do script. E falhará pelo mesmo motivo: por falta de inicialização do contador.

A questão passa a ser, então, onde colocar a inicialização que falta.  Se voce abrir esta página no editor html do Mozilla e examinar as propriedades do botão acima, ou se localizar o script do botão no código-fonte correspondente, verá que o script do botão não contém um comando de inicialização.  E que se voce tentar colocar esta inicialização no script do botão da sua página, algo como
... onClick="javascript:
    n = 0;
    n = n + 1;
    b.value = 'Esta é a ' + n + 'a. vez que vc clica!';
  " ...
verá que o erro do interpretador desaparece mas o contador ainda não funciona. Não funciona como esperado agora no sentido de que a contagem não passa do primeiro valor.  Por que? E por que o script do botão deste roteiro funciona sem nenhuma inicialização dentro dele? Será que o script do botão deste roteiro inclui algum truque que dispense a inicialização do contador?

Primeiro: observe algumas coisas sobre o uso de um contador mesmo que ainda "capenga", como o do script acima. Se o seu script estiver como acima (a atualização do contador antes do uso do mesmo na legenda do botão), a inicialização do contador (mesmo funiconado apenas na primeira clicada) deve ser com n = 0; ao passo que se voce inverter a ordem entre atualização e uso do contador (na face do botão), a inicalização deve ser n = 1, conforme explicado na semântica do contador.

Segundo: voce pode se perguntar "Mas afinal, por que o contador no script acima não funciona além da primeira contagem, e o deste roteiro funciona?" O script acima não funciona porque a regra semântica sobre a inicialização está sendo violada. A inicialização deve ocorrer, e deve ocorrer antes, não dentro do trecho de código cuja execução precisa ser contada. Do jeito que está no script acima, a cada clique a contagem anterior estará sendo destruída por uma nova inicialização, antes que possa ser atualizada.

Precisamos colocar a inicialização do contador fora do script do botão. Mas como? Obviamente, em um outro script. E mais: em um outro script que seja executado antes de qualquer possível clicagem no botão. Para isto, a diretiva <script> vem a calhar. Vem a calhar porque o script que estiver na extensão de uma instância desta diretiva será executado assim que a interpretação html atingir esta instância.. Bastaria então colocar uma tal instância, contendo a inicialização do contador, antes da diretiva html que desenha o formulário com seu botão.

Se escolhermos o cabeçalho do documento html (a extensão da diretiva <head>) para colocarmos uma instância da diretiva <script>, teremos assegurado que sua execução acontecerá antes mesmo que qualquer elemento html da página (inclusive o nosso botão) tenha sido sequer desenhado. Portanto, a melhor forma de inicializarmos nosso contador seria
<html>
<head><title>botao com memoria</title>
<script language="javascript">
     // inicialização do contador n
</script>
</head>
<body>
<form>
...
Depois de encontrar a inicialização do contador no código fonte deste roteiro, para se certificar de que aqui não há aqui truques, restaria, ainda, uma pequena dúvida: como temos certeza que o script do botão, no corpo do documento, irá tomar conhecimento de uma variável definida em outro script, que tenha executado antes dele? Para termos esta garantia, em se tratando de scripts javascript que estão no mesmo código-fonte (no mesmo documento html), basta fazer a declaração explicitamente, como foi feita neste roteiro. Ou seja, com o uso da palvra chave var.
...
<script language="javascript">
     var n = ____ // valor adequado p/ inicialização de n
             // (conforme a ordem entre uso e atualização)
</script>
...
Agora que voce já sabe tudo (ou quase tudo) sobre contadores em javascript, faça o


Exercicio 9.2

Faça o botão do seu formulário funcionar como o deste roteiro! E aproveite para observar o que acontece quando voce recarrega sua página.