Aprenda
a programar com o
C++ Builder
Neste mês, veremos a função
loop e criaremos um contador de palavras para o nosso Bloco de
Notas
4a parte
Repetição
é uma das artes fundamentais em programação.
Se estiver escrevendo um aplicativo de uma folha de pagamento,
por exemplo, talvez você queira calcular o salário
de cada empregado da empresa - do simples faxineiro até
o diretor executivo. Em um jogo de exploração espacial,
você precisará mover a espaçonave através
da tela reposicionando cada gráfico da, digamos, posição
0 à esquerda da tela, para a largura total ocupada na tela.
Essas
operações são essencialmente repetitivas.
Em termos de programação, nós podemos imaginar
isso como um looping. Presumindo que o número total de
empregados de uma empresa seja 99, então, o looping da
folha de pagamento poderia rodar do empregado 1 ao empregado 99.
Uma das construções de looping amplamente utilizadas
em C++ é o loop For. Aqui está um exemplo simples:
for
(int i = 1; i < 100 ; i++) {
calcsalarioparaempregado( i );
}
O
código entre os parênteses depois de "for"
é utilizado para controlar a execução do
loop. Esse código é dividido em três partes.
A primeira parte inicia uma variável. Aqui, uma variável
interna, "i", foi determinada com o valor 1:
int
i = 1;
Perceba
o ponto e vírgula, o qual divide cada seção
do controle do enunciado da próxima seção.
A segunda seção contém um teste:
i
< 100;
O
loop será executado enquanto esse teste permanecer verdadeiro.
Aqui, o teste determina que o valor da variável i deve
ser menor do que (<) 100. Em outras palavras, a empresa possui
99 funcionários e, então, o looping roda de 1 a
99, realizando alguns cálculos para cada empregado, um
após o outro. O código dentro do loop chama uma
função nomeada de calcsalarioparaempregado(),
passando-lhe o número do empregado, i. O código
para essa função não está presente
- utilizamos isso meramente para ilustrar um princípio.
Para facilitar, apenas imagine que o programador já escreveu
esse código.
Finalmente, aumentamos o valor de i para cada volta do loop. Isso
é feito na terceira parte do controle de loop do enunciado:
i++
É
importante acrescer o valor de i não apenas para assegurar
que um número diferente de empregados será utilizado
em cada volta do loop, mas, também, para certificar-se
de que o loop eventualmente chegará a um fim. Se você
esquecer de aumentar o valor de i, então, o teste i
< 100 não deverá falhar, desde que i sempre
retenha seu valor inicial de 1. Nesse caso, o loop deverá
rodar para sempre - ou, pelo menos, até o seu PC quebrar.
Esquecer de determinar uma condição que finalize
um loop pode causar uma série de bugs na aplicação,
portanto, fique atento!
Podemos parafrasear o código de loop mostrado acima da
seguinte forma: "Torne i igual a 1". Para demonstrar
que esse loop é executado 99 vezes, crie uma nova aplicação
e arraste um Button e um controle RichEdit (ele
está na paleta Win32) para o formulário.
Dê um duplo clique no botão e edite o seu método
Click como mostramos a seguir:
void_fastcall
TForm1::Button1Click(TObject*Sender)
{
for (int i = 1; i < 100 / i++)
RichEdit1->Lines->Add( IntToStr(i) );
}
Agora,
execute o programa, clique no botão e desça até
o box RichEdit para verificar se o valor final de i é 99.
(legenda
da foto no canto esquerdo da página):
Contando
palavras
Agora,
veremos como podemos utilizar um loop For em um exemplo
de trabalho mais funcional. Abra o projeto StrUtils.bpr
(disponível na pasta C++ Tutorial, no CD2). Ele
demonstra maneiras de se utilizar os loops como repetição
entre uma string. Nossa meta mais ousada será escrever
um contador de palavras. Antes de fazermos isso, entretanto, precisaremos
considerar como palavras individuais poderão ser extraídas
de uma string. Um contador de palavras simples pode apenas contar
os espaços entre as palavras. Entretanto, as strings nem
sempre são divididas de forma clara por simples espaços.
Considere esse exemplo:
"
Eu sou um programador-e Estou feliz!"
Essa
string possui espaços simples entre as palavras. Mas possui,
também, um espaço principal antes da primeira palavra
e não menos do que quatro espaços entre o "e"
e o "Estou". Se fôssemos contar os espaços,
chegaríamos ao número quinze. Mas existem apenas
sete palavras, então, nosso total estaria errado.
Quem sabe, poderíamos escrever uma regra especial para
omitir os espaços iniciais ou espaços repetidos?
Porém, nesse caso, encontraríamos um problema, visto
que existe um hífen entre "programador"
e "e", em vez de um espaço, o que poderá
ser interpretado como uma única palavra. E a última
palavra, "feliz", é seguida de um ponto
de exclamação e não de um espaço.
Como resultado, contaríamos apenas cinco das sete palavras.
Esse é o exemplo de um tipo de problema que ocorre todo
o tempo em programação. Uma coisa que parece bastante
simples no início, rapidamente, torna-se extremamente complexa
quando você considera todas as possibilidades de erro. Em
vez de escrever infindáveis bits de códigos - um
para cuidar dos hífens, outro para cuidar dos pontos de
exclamação e por aí vai - é melhor
ver se existe a possibilidade de reduzir a complexidade geral
do problema.
Vamos ver se o C++ Builder possui alguma rotina existente que
possa nos ajudar. Estamos lidando com Strings (um tipo de dados
de AnsiString), então, a primeira coisa a se fazer é
consultar o nome AnsiString na opção Help.
Clique no link Methods para ver quais são os métodos
disponíveis. Desça pela lista, e você verá
um método chamado IsDelimiter(). De acordo com a
descrição, ele pode determinar três parâmetros
- uma string inicial, uma string que "delimita" caracteres
e um índice dentro da string inicial. Se um dos delimitadores
for encontrado no índice especificado, o IsDelimiter()
retornará um valor verdadeiro, do contrário, ele
retornará um valor falso.
O método IsDelimiter() parece exatamente o que estamos
procurando. Ele nos permitirá declarar uma string de delimitações
contendo caracteres de pontuação, como aspas, ponto
final e hífens, em acréscimo aos espaços
entre os caracteres. Dessa forma, poderemos configurar um loop
For para criar uma repetição pela string inicial,
analisando os caracteres em cada posição para definir
o que deve e o que não deve ser delimitado.
Vamos ver como isso funciona. Olhe o código em TForm1::FindDelimsBtnClick().
Aqui, a variável "s" está iniciada
com uma string de exemplo. Assim, o loop For faz a repetição
através de "s" pelo número inicial
1 até o caractere final da string, acrescentando o controle
de variável, "i", após cada volta
do loop. Veja a condição do teste:
i
<= s.Lenght()
O
método da AnsiString Lenght() retorna o número
de caracteres na String. A operação <= significa
"menor ou igual a". Então, quando "i"
for maior do que s.Lenght(), o teste falha, e o loop termina.
Dentro do loop, existe esse simples teste:
if
( s.IsDelimiter(DELIMS, i))
A
cada volta do looping, o valor de "i" aumenta,
então, o código acima testa cada caractere na string
"s" para verificar se eles coincidem com qualquer
um dos caracteres declarados na constante DELIMS. O uso
das constantes foi explicado no mês passado. A constante
DELIMS é declarada no topo da unidade do código.
Examine esse código cuidadosamente e, então, execute
a aplicação, clicando no botão Find Delimiters
para verificar os resultados.
Um
Bloco de Notas mais eficiente
Você não precisa ser um gênio de programação
para reeditar o Bloco de Notas |
![](/file/21651/Pce29cd.iso/Interface/HTML/Materias/Imagens/Tutoriais/PCA93.cbuilder.walk1.jpg)
1 - Neste mês, voltamos ao nosso aplicativo de Editor
de Textos. Adicionamos novos itens no menu para contar palavras
e salvar arquivos. |
![](/file/21651/Pce29cd.iso/Interface/HTML/Materias/Imagens/Tutoriais/PCA93.cbuilder.walk2.jpg)
2 - Aqui, clicamos no item do menu Word Count para editar
o código associado. As palavras serão contadas
dentro dos loops. |
3
- Esse é o Editor de Textos em ação.
Quando você clicar no item do menu Word Count, essa
caixa de diálogo pop-up exibirá o número
de palavras e linhas. |
Conhecendo
os loops While e Do
O loop For é ideal para contar através de
um número fixo de repetições. Outros tipos
de loop são melhores quando a condição final
é incerta sobre as vezes em que o loop é executado.
Por exemplo, você pode querer executar um loop para contar
o número de arquivos em um disco. Até que o loop
esteja completo, você não saberá com quantos
arquivos está lidando, então, você não
poderá especificar o valor no loop For. Nesse caso,
poderíamos aplicar um loop While ou Do.
Vamos olhar primeiro para o loop Do. Ele executa qualquer
código seguido de uma palavra-chave "do"
e termina com a palavra-chave "while" seguida
de uma condição de término. Encontre o método
ReverseBtnClick(). Ele constrói uma string, s2,
colocando cada caractere em seqüência para a string
s e adicionando-as ao começo de s2. No final,
s2 será o inverso de s1. Os caracteres são
obtidos utilizando-se o método s.SubString(i,1),
onde i é a posição de uma string de
caracteres em s, e 1 é o número de
caracteres a serem copiados. Nesse caso, um único caractere
é copiado por vez.
Há um problema com o loop Do. O código dentro
dele é executado "à força" pelo
menos uma vez antes que a condição de finalização
expire. Em alguns casos, isso é inconveniente. Aqui, por
exemplo, o código pode ser danificado se s possuir
uma string vazia no início. É por isso que fomos
obrigados a adicionar um teste inicial:
if ( s.Lengh() > 0 )
Existe,
também, um loop alternativo chamado While, no qual
a condição do teste aparece no início do
loop. Se a condição falhar, então, o loop
nunca será executado completamente. Na maioria dos casos,
um loop While fornece um controle melhor do que o loop
Do. No restante desta série, nós utilizaremos
sempre o loop While no lugar de Do.
Como exemplo de um loop While, localize o método CountWordsBtnClick().
Ele executará o loop enquanto o valor de i for menor
ou igual à duração da string s:
while
( i <= s.Lengh() )
Desde
que i inicie com o valor 1, o loop nunca será
executado se a string s estiver vazia e possuir o comprimento
igual a 0. Agora, veja o código que conta as palavras.
Ele assume a forma de mais dois loops While, os quais são
executados dentro do loop While principal. O primeiro desses
loops é executado tão logo o próximo caractere
em s for um delimitador. É claro, se não
houver delimitadores até esse ponto em s, o loop
não será executado. Tão logo um "não-delimitador"
for localizado, o controle passará para o próximo
loop While. Isso será executado quando o próximo
caractere não for um delimitador (lembre-se que, em C++,
o sinal de exclamação significa "não").
Em outras palavras, o loop passará por caracteres alfanuméricos
(por exemplo, uma palavra como "olá"), mas cessará
o looping quando encontrar caracteres delimitadores (como um espaço
ou ponto final). O controle passará para o seguinte código:
wordnum++
Isso
simplesmente adiciona 1 ao valor da variável wordnum.
Então, o loop While externo será executado
novamente, e toda a operação continuará até
que outra palavra seja encontrada. Nós escrevemos um código
extra para exibir o que está acontecendo em cada volta
desses loops. Execute o projeto e clique no botão Count
Words. Examine cuidadosamente a saída até que você
tenha certeza de que entendeu como o código funciona.
Pulando
pelos loops
Existem alguns problemas aqui, entretanto. Tente apagar todo o
texto e entre com novos delimitadores, como a passagem de um assunto
para outro sem conexão lógica entre as partes, '...'.
Agora, quando você clicar no botão Count Words,
ele exibirá um total de 1 palavra. Ele deveria exibir
0. Isso acontece porque a extensão da string de saída
não é 0, então, nosso loop While externo
é executado uma vez, e o valor da variável wordnum
é acrescido de 1.
Como podemos consertar isso? Bem, olhando para o código
existente, é difícil encontrar uma solução
simples. O problema é que o código no método
CountWordsBtnClick() já se tornou muito complicado
para ser entendido facilmente. Um loop While executado
por vez já é mais do que suficiente. Tentar trabalhar
com o fluxo de execução de dois loops While
alojados dentro de outro loop While é extremamente
difícil.
Em vez de arrancar fora esse código existente, é
preferível reescrevê-lo para manter a clareza. Isso
foi o que fizemos no projeto seguinte, StrUtils2.bpr. Dessa
vez, você perceberá que movemos cada um dos dois
loops While alojados em suas próprias funções
separadas, chamadas scrollthroughdelims() e scrollthroughword().
Isso simplifica bastante o código no método CountWordsBtnClick().
Se você der uma olhada no método scrollthroughdelims(),
verá que o parâmetro i foi declarado como
um "E" comercial (&):
int
&i
Isso
é importante. Quando precedido por um "E" comercial,
o argumento consulta a variável original no código
chamado. Nós chamamos isso de argumento "referencial"
ou "de referência". Se o valor de i for alterado
em scrollthroughdelims(), então, o valor de i
também será alterado no método CountWordsBtnClick().
Um argumento declarado sem o "E" comercial nada mais
é do que uma cópia do valor da variável original.
Qualquer alteração feita nesse argumento não
afetará a variável original. Se esse é um
conceito novo para você, o código no projeto Scope.bpr
deverá ajudá-lo a entender o que está acontecendo.
A função scrollthroughword() possui dois
argumentos "referenciais":
String
&w, int &i
Aqui,
a palavra que foi construída durante a execução
da função foi determinada para a variável
w. Veja o código de chamada em CountWordsBtnClick():
scrollthroughword(
s, w, i);
Uma
vez executada, a variável w em CountWordsBtnClick()
irá conter a palavra que foi contraída dentro da
função scrollthroughword(). Podemos, subseqüentemente,
exibir essa palavra no box RichEdit chamado OutputRE:
OutputRE->Lines->Add("!!!
Word =" + w );
Nós
também podemos testar a extensão dessa palavra.
Se ela for uma string vazia, sua extensão será 0.
Nesse caso, nosso código não adicionará a
variável wordnum. E isso corrigirá o bug
do projeto anterior:
If
(w.length() > o)
wordnum++;
Faça
um teste. Execute o programa e remova a string de texto do campo
de edição, como antes. Agora, entre com alguns delimitadores,
como reticências, e clique no botão Count Words.
Dessa vez, o total correto, 0, retornará.
Tendo codificado uma função para contar palavras,
é hora de colocá-la para funcionar no editor de
textos que criamos no início desta série de matérias.
Abra o projeto TextEd.bpr. Nós adicionamos um menu
Word Count logo abaixo do menu Edit. O código
associado a esse menu usa a mesma técnica de contar palavras
desenvolvida anteriormente. A única diferença é
que o código está contido dentro de um loop For,
que é repetido em todas as linhas no controle RichEdit,
contando as palavras em cada linha. Tente abrir o arquivo Testfile.txt
e contar as palavras.
Ele pode não ser um grande rival para o Microsoft Word,
mas o nosso humilde editor de textos possui um recurso inédito
- ele conta palavras - e o Bloco de Notas do Windows não
faz isso. Nas próximas matérias, iremos adicionar
outros recursos ao editor de textos.
Visibilidade
variável
Cada variável em um programa possui um certo "alvo"
(ou scope, em inglês) que define os lugares nos quais
ela pode ser visualizada pelo código do seu programa.
Se você declarar uma variável no topo de uma
unidade, ela pode ser utilizada e modificada por todo o código
através dessa unidade. Então, se você
precisar de uma variável loop chamada de, digamos,
"i", você pode achar simples declará-la
uma única vez e, então, deixar qualquer função
usá-la sempre que necessário. Se a variável
for desligada, entretanto, isso será uma péssima
idéia, visto que poderá causar vários
bugs desastrosos. Como
regra, é melhor declarar variáveis com poucos
"alvos". Uma variável i declarada dentro
de uma função, por exemplo, não será
visível fora dessa função. Da mesma forma,
uma variável declarada dentro de um loop For
não será vista fora desse loop. Se você
acha o "alvo" difícil de entender, execute
o projeto Scope.bpr. Ele possui exemplos de variáveis
com alvos internos e externos. |
|