Anteriores | Ediotrial | Pirataria de Software | Cartas do Leitor | Mapa do CD |
Suporte | Configuração Mínima | Novidades do Mercado

Tutorial

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

1 - Neste mês, voltamos ao nosso aplicativo de Editor de Textos. Adicionamos novos itens no menu para contar palavras e salvar arquivos.

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.

Anteriores | Ediotrial | Pirataria de Software | Cartas do Leitor | Mapa do CD |
Suporte | Configuração Mínima | Novidades do Mercado

«Voltar ao topo da página»

© 2001 CD Expert. Todos os direitos reservados
.