Possíveis duplicatas:
While vs. Do While
Quando devo usar do-while em vez de while loops?
Eu tenho programado há um tempo (2 anos de trabalho + 4,5 anos de graduação + 1 ano de pré-faculdade), e nunca usei um loop do-while que não seja forçado no curso de Introdução à Programação. Tenho uma sensação crescente de que estou programando errado se nunca encontrar algo tão fundamental.
Será que simplesmente não encontrei as circunstâncias corretas?
Quais são alguns exemplos em que seria necessário usar um do-while em vez de um while?
(Minha formação foi quase toda em C / C ++ e meu trabalho é em C #, portanto, se houver outra linguagem em que absolutamente faça sentido porque o fazer enquanto funciona de maneira diferente, essas questões não se aplicam realmente.)
Para esclarecer ... eu sei a diferença entre a while
e a do-while
. Enquanto verifica a condição de saída e, em seguida, executa as tarefas. do-while
executa tarefas e verifica a condição de saída.
Respostas:
Se você sempre deseja que o loop seja executado pelo menos uma vez. Não é comum, mas eu uso de vez em quando. Um caso em que você pode querer usá-lo é tentar acessar um recurso que pode exigir uma nova tentativa, por exemplo
do { try to access resource... put up message box with retry option } while (user says retry);
fonte
do-while é melhor se o compilador não for competente em otimização. do-while tem apenas um único salto condicional, ao contrário de for e while que têm um salto condicional e um salto incondicional. Para CPUs que têm pipeline e não fazem previsão de branch, isso pode fazer uma grande diferença no desempenho de um loop fechado.
Além disso, como a maioria dos compiladores é inteligente o suficiente para realizar essa otimização, todos os loops encontrados no código descompilado normalmente serão do-while (se o descompilador ao menos se preocupar em reconstruir loops a partir de gotos locais anteriores).
fonte
Eu usei isso em uma função TryDeleteDirectory. Era algo assim
do { try { DisableReadOnly(directory); directory.Delete(true); } catch (Exception) { retryDeleteDirectoryCount++; } } while (Directory.Exists(fullPath) && retryDeleteDirectoryCount < 4);
fonte
while
?Faça while é útil quando você deseja executar algo pelo menos uma vez. Como um bom exemplo para usar do while vs. while, digamos que você queira fazer o seguinte: Uma calculadora.
Você poderia abordar isso usando um loop e verificando após cada cálculo se a pessoa deseja sair do programa. Agora você provavelmente pode presumir que, uma vez que o programa seja aberto, a pessoa deseja fazer isso pelo menos uma vez, então você pode fazer o seguinte:
do { //do calculator logic here //prompt user for continue here } while(cont==true);//cont is short for continue
fonte
continue
é uma palavra-chave; D== true
Não é necessário.cont == true
se cont for verdadeiro, retorna verdadeiro. Isso é totalmente inútil.Essa é uma resposta meio indireta, mas essa pergunta me fez pensar sobre a lógica por trás dela e pensei que valeria a pena compartilhar isso.
Como todo mundo já disse, você usa um
do ... while
loop quando deseja executar o corpo pelo menos uma vez. Mas em que circunstâncias você gostaria de fazer isso?Bem, a classe de situações mais óbvia que posso pensar seria quando o valor inicial ("não ativado") da condição de verificação é o mesmo de quando você deseja sair . Isso significa que você precisa executar o corpo do loop uma vez para preparar a condição para um valor não existente e, em seguida, executar a repetição real com base nessa condição. Com os programadores sendo tão preguiçosos, alguém decidiu encerrar isso em uma estrutura de controle.
Portanto, por exemplo, a leitura de caracteres de uma porta serial com um tempo limite pode assumir a forma (em Python):
response_buffer = [] char_read = port.read(1) while char_read: response_buffer.append(char_read) char_read = port.read(1) # When there's nothing to read after 1s, there is no more data response = ''.join(response_buffer)
Observe a duplicação do código:
char_read = port.read(1)
. Se Python tivesse umdo ... while
loop, eu poderia ter usado:do: char_read = port.read(1) response_buffer.append(char_read) while char_read
O benefício adicional para idiomas que criam um novo escopo para loops:
char_read
não polui o namespace da função. Mas observe também que há uma maneira melhor de fazer isso, e é usando oNone
valor do Python :response_buffer = [] char_read = None while char_read != '': char_read = port.read(1) response_buffer.append(char_read) response = ''.join(response_buffer)
Portanto, aqui está o ponto crucial do meu ponto: em linguagens com tipos anuláveis, a situação
initial_value == exit_value
surge muito menos freqüência, e que pode ser por isso que você não encontrá-lo. Não estou dizendo que isso nunca aconteça, porque ainda há momentos em que uma função retornaráNone
para significar uma condição válida. Mas, na minha opinião apressada e brevemente considerada, isso aconteceria muito mais se as linguagens que você usou não permitissem um valor que significa: esta variável ainda não foi inicializada.Este não é um raciocínio perfeito: na realidade, agora que os valores nulos são comuns, eles simplesmente formam mais um elemento do conjunto de valores válidos que uma variável pode assumir. Mas, na prática, os programadores têm uma maneira de distinguir entre uma variável estar em estado sensível, que pode incluir o estado de saída do loop, e ela estar em um estado não inicializado.
fonte
None
, awhile
versão corretamente não adiciona nada ao buffer de resposta, mas suado
versão sempre adiciona algo.read()
não deve retornar nuncaNone
. Se você estiver usando uma biblioteca onde existe, a premissa não se aplica (ou seja, que o valor sentinela não está no conjunto de valores de verificação possíveis).''
vez deNone
. Algum tipo de valor que avalia como falso. Atribui isso à minha falta de familiaridade com aread
função do Python .read()
retornar''
, nada será acrescentado à string, ela está vazia.Eu os usei bastante quando estava na escola, mas não tanto desde então.
Em teoria, eles são úteis quando você deseja que o corpo do loop seja executado uma vez antes da verificação da condição de saída. O problema é que, para os poucos casos em que não desejo a verificação primeiro, normalmente desejo a verificação de saída no meio do corpo do loop, e não no final. Nesse caso, prefiro usar o conhecido
for (;;)
com umif (condition) exit;
lugar no corpo.Na verdade, se estou um pouco instável na condição de saída do loop, às vezes acho útil começar a escrever o loop como um
for (;;) {}
com uma instrução de saída quando necessário e, então, quando terminar, posso ver se pode ser " limpo "movendo as inicializações, condições de saída e / ou código de incremento dentrofor
dos parênteses de.fonte
for(;;)
a "loop infinito",while(true)
mas preciso parar e pensar a respeito. Talvez seja porque eu estava codificando C antes de haver umtrue
. Antigamente tínhamos apenasTRUE
uma macro, e se algo estivesse com erros, eu teria que ir me convencer de que a macro não foi redefinida de0
alguma forma (aconteceu!). Comfor(;;)
não há chance disso. Se você insiste na forma while, eu quase prefiro verwhile (1)
. É claro que alguns podem se perguntar se não é a letra l ...codeify
texto, pode simplesmente digitá-lo diretamente no campo de comentários. Já vi algumas pessoas colocarem hiperlinks, mas isso é um pouco pesado demais para mim.Uma situação em que você sempre precisa executar um trecho de código uma vez e, dependendo do resultado, possivelmente mais vezes. O mesmo pode ser produzido com um
while
loop regular também.rc = get_something(); while (rc == wrong_stuff) { rc = get_something(); } do { rc = get_something(); } while (rc == wrong_stuff);
fonte
do while
é se você deseja executar o bloco de código pelo menos uma vez.while
por outro lado, nem sempre será executado dependendo dos critérios especificados.fonte
É simples assim:
pré-condição vs pós-condição
Agora que você conhece o segredo .. use-os com sabedoria :)
fonte
Vejo que essa pergunta foi respondida adequadamente, mas gostaria de adicionar este cenário de caso de uso muito específico. Você pode começar a usar do ... enquanto com mais freqüência.
do { ... } while (0)
é freqüentemente usado para #defines multilinhas. Por exemplo:
#define compute_values \ area = pi * r * r; \ volume = area * h
Isso funciona bem para:
-mas- há uma pegadinha para:
if (shape == circle) compute_values;
conforme isso se expande para:
if (shape == circle) area = pi *r * r; volume = area * h;
Se você envolvê-lo em um loop do ... while (0), ele se expande corretamente para um único bloco:
if (shape == circle) do { area = pi * r * r; volume = area * h; } while (0);
fonte
As respostas até agora resumem o uso geral do do-while. Mas o OP pediu um exemplo, então aqui está um: Obtenha a entrada do usuário. Mas a entrada do usuário pode ser inválida - então você pede uma entrada, valida-a, prossiga se for válida, caso contrário, repita.
Com do-while, você obtém a entrada enquanto a entrada não é válida. Com um loop while regular, você obtém a entrada uma vez, mas se for inválida, você a obterá novamente e novamente até que seja válida. Não é difícil perceber que o primeiro é mais curto, mais elegante e mais simples de manter se o corpo da alça ficar mais complexo.
fonte
break
se a entrada estiver correta.Eu o usei para um leitor que lê a mesma estrutura várias vezes.
using(IDataReader reader = connection.ExecuteReader()) { do { while(reader.Read()) { //Read record } } while(reader.NextResult()); }
fonte
Usei um
do while
quando estou lendo um valor de sentinela no início de um arquivo, mas fora isso, não acho anormal que essa estrutura não seja muito usada -do-while
s são realmente situacionais.-- file -- 5 Joe Bob Jake Sarah Sue -- code -- int MAX; int count = 0; do { MAX = a.readLine(); k[count] = a.readLine(); count++; } while(count <= MAX)
fonte
Aqui está minha teoria de por que a maioria das pessoas (inclusive eu) preferem os loops while () {} para fazer {} while (): Um loop while () {} pode ser facilmente adaptado para funcionar como um loop do..while () enquanto o oposto não é verdade. Um loop while é de certa forma "mais geral". Os programadores também gostam de padrões fáceis de entender. Um loop while diz logo no início qual é o seu invariante e isso é uma coisa boa.
Aqui está o que quero dizer sobre a coisa "mais geral". Faça este loop do..while:
do { A; if (condition) INV=false; B; } while(INV);
Transformar isso em um loop while é simples:
INV=true; while(INV) { A; if (condition) INV=false; B; }
Agora, pegamos um modelo de loop while:
while(INV) { A; if (condition) INV=false; B; }
E transformar isso em um loop do..while, produz esta monstruosidade:
if (INV) { do { A; if (condition) INV=false; B; } while(INV) }
Agora temos duas verificações em extremidades opostas e se a invariante muda, você deve atualizá-la em dois lugares. De certa forma, faça .. enquanto é como as chaves de fenda especializadas na caixa de ferramentas que você nunca usa, porque a chave de fenda padrão faz tudo o que você precisa.
fonte
Estou programando há cerca de 12 anos e apenas 3 meses atrás encontrei uma situação em que era realmente conveniente usar do-while, pois uma iteração era sempre necessária antes de verificar uma condição. Portanto, acho que seu grande momento está à frente :).
fonte
do
while
havia uma solução melhor sem arrependimentos?Não consigo imaginar como você passou tanto tempo sem usar um
do...while
loop.Há um em outro monitor agora e vários desses loops nesse programa. Eles são todos na forma:
do { GetProspectiveResult(); } while (!ProspectIsGood());
fonte
É uma estrutura bastante comum em um servidor / consumidor:
DOWHILE (no shutdown requested) determine timeout wait for work(timeout) IF (there is work) REPEAT process UNTIL(wait for work(0 timeout) indicates no work) do what is supposed to be done at end of busy period. ENDIF ENDDO
o
REPEAT UNTIL(cond)
ser umdo {...} while(!cond)
Às vezes, a espera pelo trabalho (0) pode ser mais barata em termos de CPU (mesmo eliminar o cálculo do tempo limite pode ser uma melhoria com taxas de chegada muito altas). Além disso, existem muitos resultados da teoria das filas que tornam o número atendido em um período de grande movimento uma estatística importante. (Veja por exemplo Kleinrock - Vol 1.)
Similarmente:
DOWHILE (no shutdown requested) determine timeout wait for work(timeout) IF (there is work) set throttle REPEAT process UNTIL(--throttle<0 **OR** wait for work(0 timeout) indicates no work) ENDIF check for and do other (perhaps polled) work. ENDDO
onde
check for and do other work
pode ser exorbitantemente caro para colocar no loop principal ou talvez um kernel que não suporte um eficientewaitany(waitcontrol*,n)
operação de tipo ou talvez uma situação onde uma fila priorizada pode privar o outro trabalho e o acelerador é usado como controle de fome.Esse tipo de balanceamento pode parecer um hack, mas pode ser necessário. O uso cego de conjuntos de encadeamentos anularia inteiramente os benefícios de desempenho do uso de um encadeamento de zelador com uma fila privada para uma estrutura de dados complicada de alta taxa de atualização, pois o uso de um conjunto de encadeamentos em vez de um encadeamento de zelador exigiria uma implementação segura de encadeamento.
Eu realmente não quero entrar em um debate sobre o pseudocódigo (por exemplo, se o desligamento solicitado deve ser testado no UNTIL) ou threads de zelador versus pools de threads - isso serve apenas para dar uma ideia de um caso de uso específico de a estrutura do fluxo de controle.
fonte
Esta é minha opinião pessoal, mas esta questão implora por uma resposta enraizada na experiência:
Eu tenho programado em C por 38 anos, e nunca uso
do
/while
loops em código normal.O único uso convincente para esta construção é em macros, onde pode envolver várias instruções em uma única instrução por meio de um
do { multiple statements } while (0)
Já vi inúmeros exemplos de
do
/while
loops com detecção de erro falsa ou chamadas de função redundantes.Minha explicação para essa observação é que os programadores tendem a modelar problemas incorretamente quando pensam em termos de
do
/while
loops. Eles perdem uma condição final importante ou perdem a possível falha da condição inicial que eles movem para o final.Por essas razões, eu acredito que onde há um
do
while
loop / , há um bug , e regularmente desafio os programadores novatos a me mostrarem umdo
/while
loop onde não consigo identificar um bug nas proximidades.Este tipo de loop pode ser facilmente evitado: use ae
for (;;) { ... }
adicione os testes de terminação necessários onde forem apropriados. É bastante comum que haja mais de um teste desse tipo.Aqui está um exemplo clássico:
/* skip the line */ do { c = getc(fp); } while (c != '\n');
Isso falhará se o arquivo não terminar com uma nova linha. Um exemplo trivial de tal arquivo é o arquivo vazio.
Uma versão melhor é esta:
int c; // another classic bug is to define c as char. while ((c = getc(fp)) != EOF && c != '\n') continue;
Como alternativa, esta versão também oculta a
c
variável:for (;;) { int c = getc(fp); if (c == EOF || c == '\n') break; }
Tente pesquisar por
while (c != '\n');
em qualquer mecanismo de pesquisa e você encontrará bugs como este (recuperado em 24 de junho de 2017):Em ftp://ftp.dante.de/tex-archive/biblio/tib/src/streams.c , função
getword(stream,p,ignore)
, tem umdo
/while
e com certeza pelo menos 2 bugs:c
é definido como umchar
ewhile (c!='\n') c=getc(stream);
Conclusão: evite
do
/while
loops e procure por bugs quando encontrar um.fonte
while() {}
loops, porque eles são mais seguros em termos de verificações iniciais, então tendem a deixá-lo "louco", preguiçoso para pensar em um algoritmo melhor, colocar uma tonelada de checagem de erros desnecessária, etc. "Falta dedo{}while()
uso" é um sinal de mau (sem cérebro) ou de um programador novato, produz um código de qualidade inferior e mais lento. Então eu primeiro me pergunto "É estritamente um caso while () {}?" Se não - eu apenas tentarei escrever do while loop, apesar de poder exigir mais raciocínio e precisão dos dados de entradabreak
oureturn
instruções. Transformar o loopdo
/while
em umfor(;;)
loop IMHO mais consistente. Meu ostracismo em relação a essa afirmação é semelhante à minha recusa em usar,strncpy()
mesmo nos raros casos em que seria a ferramenta certa: não deixe leitores casuais imaginarem que essas construções são inofensivas, porque na maioria dos casos são fontes de insetos difíceis de encontrar .while
os loops verificam a condição antes do loop, osdo...while
loops verificam a condição após o loop. Isso é útil se você quiser basear a condição nos efeitos colaterais do loop em execução ou, como outros usuários disseram, se quiser que o loop seja executado pelo menos uma vez.Eu entendo de onde você vem, mas
do-while
é algo que raramente uso, e eu nunca usei. Você não está fazendo errado.Você não está fazendo errado. É como dizer que alguém está fazendo errado porque nunca usou o
byte
primitivo. Só não é tão comumente usado.fonte
O cenário mais comum que encontro em que uso um loop
do
/while
é em um pequeno programa de console que é executado com base em alguma entrada e se repetirá quantas vezes o usuário quiser. Obviamente, não faz sentido que um programa de console nunca seja executado; mas além da primeira vez, depende do usuário - portanto,do
/ emwhile
vez de apenaswhile
.Isso permite que o usuário experimente várias entradas diferentes, se desejar.
do { int input = GetInt("Enter any integer"); // Do something with input. } while (GetBool("Go again?"));
Suspeito que os desenvolvedores de software usam
do
/while
cada vez menos hoje em dia, agora que praticamente todo programa sob o sol tem algum tipo de interface gráfica do usuário. Faz mais sentido com aplicativos de console, pois há uma necessidade de atualizar continuamente a saída para fornecer instruções ou solicitar ao usuário novas informações. Com uma GUI, em contraste, o texto que fornece essas informações ao usuário pode apenas ficar em um formulário e nunca precisa ser repetido programaticamente.fonte
Eu uso loops do-while o tempo todo ao ler arquivos. Eu trabalho com muitos arquivos de texto que incluem comentários no cabeçalho:
# some comments # some more comments column1 column2 1.234 5.678 9.012 3.456 ... ...
Vou usar um loop do-while para ler até a linha "coluna1 coluna2" para que eu possa procurar a coluna de interesse. Este é o pseudocódigo:
do { line = read_line(); } while ( line[0] == '#'); /* parse line */
Em seguida, farei um loop while para ler o resto do arquivo.
fonte
if (line[0]=='#') continue;
logo após aread_line
chamada em seu loop while principal ...Sendo um programador geezer, muitos dos meus projetos de programação escolar usaram interações dirigidas por menu de texto. Praticamente todos usaram algo como a seguinte lógica para o procedimento principal:
do display options get choice perform action appropriate to choice while choice is something other than exit
Desde os tempos de escola, descobri que uso o loop while com mais frequência.
fonte
Um dos aplicativos que vi é no Oracle, quando olhamos os conjuntos de resultados.
Depois de ter um conjunto de resultados, primeiro você busca a partir dele (faz) e a partir desse ponto .. verifique se a busca retorna um elemento ou não (enquanto o elemento for encontrado ..) .. O mesmo pode ser aplicável a qualquer outro " implementações semelhantes a busca ".
fonte
Eu o usei em uma função que retornou a próxima posição do caractere em uma string utf-8:
char *next_utf8_character(const char *txt) { if (!txt || *txt == '\0') return txt; do { txt++; } while (((signed char) *txt) < 0 && (((unsigned char) *txt) & 0xc0) == 0xc0) return (char *)txt; }
Observe que esta função foi escrita da mente e não foi testada. O ponto é que você tem que dar o primeiro passo de qualquer maneira e tem que fazer antes de poder avaliar a condição.
fonte
*(unsigned char *)txt-0x80U<0x40
.Qualquer tipo de entrada de console funciona bem com do-while porque você pergunta na primeira vez e faz a solicitação novamente sempre que a validação de entrada falha.
fonte
Mesmo que haja muitas respostas, aqui está a minha opinião. Tudo se resume à otimização. Vou mostrar dois exemplos em que um é mais rápido que o outro.
Caso 1:
while
string fileName = string.Empty, fullPath = string.Empty; while (string.IsNullOrEmpty(fileName) || File.Exists(fullPath)) { fileName = Guid.NewGuid().ToString() + fileExtension; fullPath = Path.Combine(uploadDirectory, fileName); }
Caso 2:
do while
string fileName = string.Empty, fullPath = string.Empty; do { fileName = Guid.NewGuid().ToString() + fileExtension; fullPath = Path.Combine(uploadDirectory, fileName); } while (File.Exists(fullPath));
Portanto, dois farão exatamente as mesmas coisas. Mas há uma diferença fundamental: o while requer uma instrução extra para inserir o while. O que é feio, porque digamos que todos os cenários possíveis da
Guid
classe já foram escolhidos, exceto por uma variante. Isso significa que terei que dar uma volta por5,316,911,983,139,663,491,615,228,241,121,400,000
cima. Toda vez que eu chegar ao final do meu extrato while, terei de fazer astring.IsNullOrEmpty(fileName)
verificação. Portanto, isso ocuparia um pouco, uma pequena fração do trabalho da CPU. Mas faça esta tarefa muito pequena vezes as combinações possíveis doGuid
turma tem e estamos falando de horas, dias, meses ou horas extras?Claro que este é um exemplo extremo porque você provavelmente não veria isso na produção. Mas se pensarmos no algoritmo do YouTube, é bem possível que eles encontrem a geração de um ID onde alguns IDs já foram obtidos. Portanto, tudo se resume a grandes projetos e otimização.
fonte
Eu gosto de entender esses dois como:
while
-> 'repetir até',do ... while
-> 'repetir se'.fonte
Eu encontrei isso enquanto pesquisava o loop adequado para usar em uma situação que tenho. Acredito que isso irá satisfazer totalmente uma situação comum em que um loop do .. while é uma implementação melhor do que um loop while (linguagem C #, já que você declarou que é o seu principal para o trabalho).
Estou gerando uma lista de strings com base nos resultados de uma consulta SQL. O objeto retornado por minha consulta é um SQLDataReader. Este objeto tem uma função chamada Read () que avança o objeto para a próxima linha de dados e retorna true se houver outra linha. Ele retornará falso se não houver outra linha.
Usando essas informações, quero retornar cada linha a uma lista e, em seguida, parar quando não houver mais dados para retornar. Um loop Do ... While funciona melhor nesta situação, pois garante que a adição de um item à lista acontecerá ANTES de verificar se há outra linha. A razão pela qual isso deve ser feito ANTES de verificar o while (condição) é que quando ele verifica, ele também avança. O uso de um loop while nesta situação faria com que ele ignorasse a primeira linha devido à natureza dessa função específica.
Em resumo:
Isso não funcionará na minha situação.
//This will skip the first row because Read() returns true after advancing. while (_read.NextResult()) { list.Add(_read.GetValue(0).ToString()); } return list;
Isso vai.
//This will make sure the currently read row is added before advancing. do { list.Add(_read.GetValue(0).ToString()); } while (_read.NextResult()); return list;
fonte
Read()
e deve ser um loop while, já que a primeiraRead()
chamada acessa a primeira linha. O externo chamaNextResult()
e deve ser um do-while, porque o primeiro conjunto de dados de resultado está ativo antes da primeiraNextResult()
chamada. Os snippets de código não fazem muito sentido, especialmente porque os comentários não correspondem ao código e estão errados.Console.WriteLine("hoeveel keer moet je de zin schrijven?"); int aantal = Convert.ToInt32(Console.ReadLine()); int counter = 0; while ( counter <= aantal) { Console.WriteLine("Ik mag geen stiften gooien"); counter = counter + 1;
fonte