2. Estamento nobiliario
2.2.1. Los Truyols en la Procuración Real
Para introduzir uma nova estrutura de repetição e cotejá-la com o comando for, considere um programa para encontrar um divisor próprio de um inteiro dado (um divisor próprio de um inteiro n é um divisor de n diferente dele e de 1). Esta questão é importante na verificação da primalidade de um inteiro: um número que não tem divisores próprios é dito primo. Com a utilização do comando for teríamos a seguinte solução para esta questão.
/*Programa que determina um divisor próprio de um inteiro */ #include <stdio.h>
main() {
int Num, i, Divisor;
printf("Digite um numero: "); scanf("%d", &Num); Divisor = 0; for (i = 2; i < Num; i = i + 1) if (Num % i == 0) Divisor = i; if (Divisor != 0)
printf("%d é divisor próprio de %d \n", Divisor, Num); else
printf("%d não tem divisores próprios \n", Num); }
Um problema com este programa é que ele retorna sempre, se existir, o maior divisor próprio. Isto significa que se a entrada for um número par a estrutura de repetição não é interrompida quando o divisor 2 é
encontrado, o que, evidentemente, vai prejudicar a performance do programa. Isto pode ser contornado pois os compiladores C permitem que uma variável de controle de um comando for tenha o seu conteúdo alterado dentro do próprio comando. Com isto, o programa acima ficaria da seguinte forma.
#include <stdio.h> main()
{
int Num, i, Divisor;
printf("Digite um número inteiro: "); scanf("%d", &Num); Divisor = 0; for (i = 2; i < Num; i = i + 1) if (Num % i == 0) { Divisor = i; i = Num; } if (Divisor != 0)
printf("%d e' divisor próprio de %d \n", Divisor, Num); else
printf("%d não tem divisores próprios \n", Num); }
Nesta versão, quando o primeiro divisor próprio é encontrado, o comando i = Num; faz com que a execução do comando for seja interrompida. A prática de encerrar um comando for através da alteração do conteúdo da variável de controle não será aqui incentivada pelo fato de que isto desestrutura o programa, dificultando sua legibilidade. Além disso, há situações em que não se pode conhecer o número máximo de repetições de uma estrutura de repetição. Na verdade, a questão central é que o comando for deve ser utilizado quando o número de repetições de execução de uma sequência de comandos é conhecido a priori. Quando isto não acontece (que é o caso do exemplo anterior: não se sabe a priori se e quando um divisor próprio vai ser encontrado), deve-se usar o comando while, que possui a seguinte sintaxe:
while (Expressão) {
Sequência de comandos }
sendo os delimitadores opcionais se a sequência possui um só comando (como acontece nas outras
estruturas de repetição).
A semântica deste comando é óbvia: a sequência de comandos é executada enquanto o valor da Expressão for diferente de zero. Naturalmente, pode ocorrer que a sequência de comandos não seja executada nenhuma vez, isto ocorrendo se o valor da Expressão for igual a zero quando da "primeira" execução do comando (o teste é feito antes da execução da sequência de comandos). Por outro lado, é necessário que um dos comandos da sequência de comandos altere conteúdos de variáveis que aparecem na Expressão de modo que em algum instante ela se torne igual a zero. Do contrário, a sequência de comandos terá sua execução repetida indefinidamente, o programa nunca termina e, evidentemente, não executa a tarefa para a qual foi desenvolvido. Quando isto acontece é comum se dizer que o programa está em looping.
Com o comando while as questões levantadas acima sobre o programa para determinar um divisor próprio de um inteiro dado são resolvidas e temos o seguinte programa:
/*Programa que determina o menor divisor próprio de um inteiro */ #include <stdio.h>
#include <conio.h> main()
{
int Num, d, Met;
printf("Digite o numero: "); scanf("%d", &Num); Met = Num/2; d = 2;
while (Num % d != 0 && d < Met) d++;
if (d <= Met)
printf("%d é divisor de %d \n", d, Num); else
printf("%d não tem divisores próprios", Num); getch();
}
Observe que, ao contrário dos exemplos anteriores, a estrutura também seria interrompida quando a variável com a qual se procura um divisor atingisse a "metade" do inteiro; isto se explica pelo fato de que se um inteiro não possui um divisor próprio menor do que sua "metade", então ele é primo. Esta versão ainda pode ser melhorada utilizando-se o fato discutido em [Evaristo, J 2002] de que se um inteiro não possui um divisor próprio menor do que ou igual a sua raiz quadrada, ele não tem divisores próprios. Levando isso em conta, teríamos o seguinte programa.
/*Programa que determina o menor divisor próprio de um inteiro*/ #include <stdio.h> #include <conio.h> #include <math.h> main() { int Num, d; float r; printf("Digite o numero: "); scanf("%d", &Num); r = sqrt(Num); d = 2;
while (Num % d != 0 && d <= r) d++;
if (d <= r)
printf("%d é divisor de %d \n", d, Num); else
printf("%d não tem divisores próprios", Num); getch();
}
Como já foi dito, um número inteiro que não tem divisores próprios é chamado número primo. Assim, o comando de saída vinculado à opção else poderia ser
printf("%d é primo", Num);
Vale observar que o comando d = 2; dos programas acima atribuiu um valor inicial à variável d. Este valor é incrementado de uma unidade enquanto um divisor não foi encontrado. Um comando de atribuição de um valor inicial a uma variável é chamado inicialização da variável e os compiladores da linguagem C permitem que inicializações de variáveis sejam feitas no instante em que elas são declaradas. Assim, as declarações de variáveis dos programas acima poderiam ter sido feitas da seguinte forma:
int Num, i, d = 2;
Neste livro, na maioria das vezes vamos optar por inicializar as variáveis imediatamente antes da necessidade. A razão desta opção é que há situações, como mostraremos no próximo exemplo, em que não se pode simplesmente inicializar uma variável quando da sua declaração.
Observe que o último comando dos últimos dois programas foi uma chamada da função getch(). Como já foi dito, a execução desta função requer a digitação de alguma tecla. Isto faz com a janela do usuário (que exibe o resultado do processamento) permaneça ativa até que uma tecla seja acionada.
Repetindo a execução de um programa
Uma outra aplicação importante do comando while diz respeito a aplicações sucessivas de um programa. O leitor deve ter observado que os programas anteriores são executados apenas para uma entrada. Se quisermos a sua execução para outra entrada precisamos executar o programa de novo.
Pode-se repetir a execução de um programa quantas vezes se queira, colocando-o numa estrutura definida por um comando while, controlada pelo valor de algum dado de entrada. Neste caso, o valor que encerra a execução pode ser informado dentro da mensagem que indica a necessidade da digitação da entrada. O programa anterior poderia ser então escrito da seguinte forma.
/*Programa que determina o menor divisor próprio de um inteiro */ #include <stdio.h> #include <conio.h> #include <math.h> main() { int Num, d; float r;
printf("Digite o numero (zero para encerrar): "); Num = 1; while (Num != 0) { scanf("%d", &Num); r = sqrt(Num); d = 2;
while (Num % d != 0 && d <= r) d++;
if (d <= r)
printf("%d é divisor de %d \n", d, Num); else
printf("%d é primo", Num); }
}
Observe que, neste caso, a variável d não pode ser inicializada quando da sua declaração. Observe também que não há necessidade da função getch(), pois a própria repetição da execução deixa a janela do usuário aberta.
Alguns programadores preferem que a repetição da execução de um programa seja determinada por uma pergunta ao usuário do tipo “Deseja continuar (S/N)?”. Neste caso, há necessidade de uma variável do tipo char para receber a resposta e controlar a repetição da execução do programa.
#include <stdio.h> #include <ctype.h> #include <conio.h> #include <math.h> main() { int Num, d; float r; char c; c = 'S'; while (toupper(c) == 'S') { printf("Digite o numero: "); scanf("%d", &Num); r = sqrt(Num); d = 2;
while (Num % d != 0 && d <= r) d++;
if (d <= r)
printf("%d é divisor de %d \n", d, Num); else
printf("%d é primo", Num); puts("Deseja continuar (S/N)?"); c = getch();
} }
Vale lembrar que a função toupper() retorna o argumento no formato maiusculo. Esta função foi ativada aqui para que o usuário não se preocupe em digitar como resposta letras maiusculas. Qualquer letra que for digitada, a função a torna maiuscula e o sistema a compara com S (maiusculo).