• Revista PROGRAMAR: Já está disponível a edição #53 da revista programar. Faz já o download aqui!

Baderous

Ciclos com break VS Ciclos com boolean

25 mensagens neste tópico

O título pode não ser muito esclarecedor mas passo a explicar. Tenho um professor que exige que utilizemos determinadas regras a programar:

- Ciclos for são usados para iterar sobre todos os elementos de um conjunto/colecção/array/wtv.

- Ciclos while e do while são usados com uma condição booleana de paragem e são usados para iterar sobre 0 ou mais elementos de um conjunto/colecção/array/wtv (os que verifiquem a condição).

- Não se deve usar break dentro de for, na tentativa de implementar um while.

Passo a citar:

Note-se que sendo a condição_de_iteração testada antes da primeira execução do corpo, pode acontecer

que, sendo a primeira imediatamente falsa, este não seja executado. Assim, a principal característica

determinante na utilização de uma estrutura repetitiva do tipo while é que a mesma conduz a 0 ou mais

iterações do corpo de instruções a iterar. O número de iterações que serão efectivamente realizadas não pode

nestes casos ser expresso apenas em função dos sucessivos valores tomados por uma variável de controlo.

Quando o número de iterações pode de facto ser expresso de tal forma, a estrutura aconselhada é uma

estrutura repetitiva do tipo for. É, no entanto, curioso verificar que os programadores, por erro conceptual

ou simples hábito que não se aconselha que seja reproduzido, implementam estruturas while através de

estruturas for, quer usando break, quer através de condições adicionais de saída na expressão de

condição_de_saída do ciclo for.

Aconselha-se vivamente que, sempre que pretendermos expressar uma iteração que pode ocorrer 0 ou mais

vezes, se expresse tal facto usando uma estrutura while. Sempre que pretendermos expressar uma iteração

que ocorre tantas vezes quantos os valores que uma dada variável deve tomar para que, qualquer que seja o

incremento, partindo de um dado valor atinja um outro, então, tal deve ser codificado usando uma estrutura

for.

Eu agora pergunto: Qual é a diferença, para além da estética e da parte conceptual, entre usar um for com um break ou usar um while? Penso que a maior diferença será ao nível do código Assembly gerado...

0

Partilhar esta mensagem


Link para a mensagem
Partilhar noutros sites

Julgo que o teu professor diz isso por razões conceptuais.

Mas há situações em que o break/condição extra facilita muito.

Exemplo: Imprimir uma matriz respeitando a seguinte regra: nenhum número numa linha deve ser impresso se tiver sido impresso um 0 nessa linha.

um código como este:

for (int i=0;i<lines;i++) {
  for (int j=0;j<cols;j++) {
    cout << m[i][j] << " ";
    if (!m[i][j]) break;
  }
  cout << endl;
}

Implementar isto com while dá um bocado mais trabalho..

0

Partilhar esta mensagem


Link para a mensagem
Partilhar noutros sites

Pois dá, mas se se justificasse a um nível para além do conceptual, eu até entendia, mas fazer isso só porque sim, não me agrada muito...

0

Partilhar esta mensagem


Link para a mensagem
Partilhar noutros sites

Não usar break ou goto são velhas "boas práticas de programação", com as quais eu concordo.

Através de condições, consegue-se sempre fazer um programa sem eles e bem estruturado.

Claro que admito que use o break as vezes, mas eu prefiro não usar.

0

Partilhar esta mensagem


Link para a mensagem
Partilhar noutros sites

Atenção que eu prefiro não usar, mas em situações como aquela que eu descrevi o break vem "limpar" o código.

Goto's nunca utilizo.

0

Partilhar esta mensagem


Link para a mensagem
Partilhar noutros sites

Hehe eu vou ser do contra. Eu gosto de usar breaks e continues, obviamente moderadamente. Mas uso bastante para criar algo tipo guardas. Acho que torna o código mais legível e mais facilmente editável a futuras alterações.

Imaginemos que tenho uma colecção de um certo tipo de objectos, e quero os processar de alguma forma conforme certas propriedades dos mesmos. Se há partida não souber que todos os meus elementos respeitam essas propriedades, só tenho duas alternativas:

- Filtrar previamente os elementos obtendo uma sub-colecção dos elementos e então é que vou iterar sobre essa para processar os elementos.

- Itero sobre todos os elementos todas da colecção mas apenas processo os que são válidos.

90% dos casos a segunda abordagem é melhor, já que aplico uma fusão dos loops.

Agora se não formos usar breaks ou continues, o código ficaria algo como:

for(Tipo x; ...) {
  if(condicao1 x || condicao 2 || (condicao3 && condicao4)) {
    O elemento x é válido
    Toda a lógica vai é inserida aqui
    ...
  }
}

Eu pessoalmente não gosto nada disto. Prefiro mais desta forma:

for(Tipo x; ...) {
    if (condicao1 x) continue;
    if (condicao2 x) continue;
    if (condicao3 x && condicao4 x) continue;

    O elemento x é válido
    Toda a lógica vai é inserida aqui
    ...
  }
}

Acho que torna o código mais legível. E qualquer alteração nos "filtros" é muito mais agradável.

Para além que detesto ver blocos de código enormes (reminiscência de Haskell??) e no 1º caso normalmente resulta em blocos de if enormes...

Agora o que eu não percebo é a diferenciação do prof perante os fors e whiles. O for é um while só tem é uma sintaxe mais agradável para o programador.

0

Partilhar esta mensagem


Link para a mensagem
Partilhar noutros sites

Acho que torna o código mais legível. E qualquer alteração nos "filtros" é muito mais agradável.

Para além que detesto ver blocos de código enormes (reminiscência de Haskell??) e no 1º caso normalmente resulta em blocos de if enormes...

Para melhorar a legibilidade nesses casos gosto mais de meter uma condição por linha, assim:

for(Tipo x; ...) {
  if(condicao1 x || 
     condicao 2 || 
     (condicao3 && condicao4)
   ) {
      O elemento x é válido
      Toda a lógica vai é inserida aqui
      ...
  }
}

Quanto às questão do for vs while há linguagens (por exemplo php) em que há uma pequena diferença performance e memória utilizada entre os dois loop's, sendo o while o loop mais "leve". No entanto são diferenças mínimas e penso que não se justifica cuidados especiais...

Eu escolho o while ou for em função do problema que tenho para resolver... se tenho de contagem uso o for, se é para testar uma condição uso o while.

0

Partilhar esta mensagem


Link para a mensagem
Partilhar noutros sites

Implementar isto com while dá um bocado mais trabalho..

Então e se for com 2 whiles? ;) ::P

Penso que assim o trabalho seria o mesmo, embora o for seja mais indicado para o caso :cheesygrin:

0

Partilhar esta mensagem


Link para a mensagem
Partilhar noutros sites

Então e se for com 2 whiles? ;) ::P

Penso que assim o trabalho seria o mesmo, embora o for seja mais indicado para o caso :cheesygrin:

Nunca ponderei implementar só com um..

Mas ficaria algo assim:

int i=0;
while (i<lines) {
  int j=-1;
  do {
    j++;
    cout << m[i][j] << " ";
  } while(m[i][j] && j+1<cols);
  i++;
  cout << endl;
}

Sinceramente, só para não usar um break, este código feio.. Só queremos varrer uma matriz!

0

Partilhar esta mensagem


Link para a mensagem
Partilhar noutros sites

Não obrigatoriamente.

int i=0, j = 0;
while (i<lines) {
  j = 0;
  do {
    cout << m[i][j++] << " ";
  } while(m[i][j] && j+1<cols);
  i++;
  cout << endl;
}

:cheesygrin:

0

Partilhar esta mensagem


Link para a mensagem
Partilhar noutros sites

Esse programa não chega a imprimir o 0 (a menos que esteja na primeira linha) e vai imprimir números depois dele se estiver na primeira posição.

Não estou a dizer que não é possível fazer mais pequeno sem breaks, mas acho que já demonstrei que HÁ casos em que dá muito jeito.

0

Partilhar esta mensagem


Link para a mensagem
Partilhar noutros sites

Já agora... brincando um pouco com while's

	
int i = -1, j = -1;

while(++i < lines){		
while(++j < cols ){
	cout << m[i][j] << " ";
}while(m[i][j]);
j = -1;
cout << endl;
}

:cheesygrin:

0

Partilhar esta mensagem


Link para a mensagem
Partilhar noutros sites

isso não é válido, nem compila..

Não tenho aqui nenhum compilador de C mesmo para testar, mas penso que é possivel fazer isso.

Mas essa construção é válida por exemplo em C# (testado :cheesygrin:)

0

Partilhar esta mensagem


Link para a mensagem
Partilhar noutros sites

Esse programa não chega a imprimir o 0 (a menos que esteja na primeira linha) e vai imprimir números depois dele se estiver na primeira posição.

Não estou a dizer que não é possível fazer mais pequeno sem breaks, mas acho que já demonstrei que HÁ casos em que dá muito jeito.

Não imprime o 0 porquê?
0

Partilhar esta mensagem


Link para a mensagem
Partilhar noutros sites

Sinceramente, só para não usar um break, este código feio.. Só queremos varrer uma matriz!

Não percebi que estavas a dizer que era sem break.

isso não é válido, nem compila..

Se leste o que estava aqui antes, esquece o que perguntei ::cheesygrin: Mas a verdade é que compila... O código é válido. Entra é em ciclo infinito... e não pára no 0.

0

Partilhar esta mensagem


Link para a mensagem
Partilhar noutros sites

Se leste o que estava aqui antes, esquece o que perguntei :;) Mas a verdade é que compila... O código é válido. Entra é em ciclo infinito... e não pára no 0.

É verdade  :-[ aquilo fica a preso no

while(m[i][j])

caso o primeiro elemento não seja 0, assim fica a imprimir 1 até mais não.. ups!

Lembrei-me dessa construção e mandei o código sem pensar bem no assunto...  :cheesygrin:

0

Partilhar esta mensagem


Link para a mensagem
Partilhar noutros sites

Ele compila porque considera isto

int i = -1, j = -1;


while(++i < lines){		
while(++j < cols ){
	cout << m[i][j] << " ";
}
        while(m[i][j]);
  j = -1;
cout << endl;
}

É-lhe indiferente ter o while na linha de cima ou na de debaixo..

Tiracio: não imprimes o 0 porque estás a: imprimir, incrementar, verificar. Ou seja, verificas antes de imprimir.

0

Partilhar esta mensagem


Link para a mensagem
Partilhar noutros sites

Warrior, a não ser que algo tenha mudado de repente, ele tem i=0, j=0, mostra m[ i][j], incrementa o j, verifica se o próximo está vazio ou não. Logo, imprime o m[0][0].

0

Partilhar esta mensagem


Link para a mensagem
Partilhar noutros sites

Não estava a falar do m[0][0], mas sim do valor "0" se estiver armazenado a meio da matriz.

Tipo isto:

7 8 9

3 0 4

1 2 3

O teu programa imprime:

7 8 9

3

1 2 3

Quando se falava nisto:

7 8 9

3 0

1 2 3

Já fizemos hijack ao tópico..

0

Partilhar esta mensagem


Link para a mensagem
Partilhar noutros sites

Tens razão, bem visto.

int i=0, j = 0;
while (i<lines) {
  j = 0;
  do {
    cout << m[i][j] << " ";
  } while(m[i][j++] && j<cols);
  i++;
  cout << endl;
}

0

Partilhar esta mensagem


Link para a mensagem
Partilhar noutros sites

Ele compila porque considera isto

int i = -1, j = -1;


while(++i < lines){		
while(++j < cols ){
	cout << m[i][j] << " ";
}
        while(m[i][j]);
  j = -1;
cout << endl;
}

É-lhe indiferente ter o while na linha de cima ou na de debaixo..

Exacto, é código sintacticamente válido mas logicamente incorrecto.

0

Partilhar esta mensagem


Link para a mensagem
Partilhar noutros sites

Outra razão para utilizar o break, é que torna o código mais rápido.

O compilador consegue optimizar melhor essa construção pelo

que torna a execução dessa parte do codigo mais rápida.

O Linus (o criador o kernel do linux) utiliza muito o break no kernel

do linux.

0

Partilhar esta mensagem


Link para a mensagem
Partilhar noutros sites

Sinceramente apenas o "goto" não é aconselhado para quem percebe de Assembly é facil entender.

Mas o "break" torna o código mais rápido (evita uma comparação a mais) e não é má programação.

0

Partilhar esta mensagem


Link para a mensagem
Partilhar noutros sites

Crie uma conta ou ligue-se para comentar

Só membros podem comentar

Criar nova conta

Registe para ter uma conta na nossa comunidade. É fácil!


Registar nova conta

Entra

Já tem conta? Inicie sessão aqui.


Entrar Agora