Às vezes eu preciso de loops que precisam de uma pausa como esta:
for(int i=0;i<array.length;i++){
//some other code
if(condition){
break;
}
}
Eu me sinto desconfortável com a escrita
if(condition){
break;
}
porque consome 3 linhas de código. E descobri que o loop pode ser reescrito como:
↓
for(int i=0;i<array.length && !condition;i++){
//some other code
}
Portanto, minha pergunta é: é uma boa prática mover a condição para o campo de condição para reduzir linhas de códigos, se possível?
coding-style
loops
ocomfd
fonte
fonte
condition
é em particular.for(int i=0;i<array.length && !condition;i++)
é relativamente incomum e pode ser negligenciada por alguém que apenas está analisando o código; esse pode ser um bom caso de uso para um loopwhile
oudo while
, que geralmente possui várias condições de interrupção na definição do loop.Respostas:
Esses dois exemplos que você deu não são funcionalmente equivalentes. No original, o teste de condição é realizado após a seção "algum outro código", enquanto na versão modificada, é feito primeiro, no início do corpo do loop.
O código nunca deve ser reescrito com o único objetivo de reduzir o número de linhas. Obviamente, é um bom bônus quando funciona dessa maneira, mas nunca deve ser feito à custa da legibilidade ou correção.
fonte
Eu não compro o argumento de que "consome 3 linhas de código" e, portanto, é ruim. Afinal, você pode escrever como:
e consuma apenas uma linha.
Se ele
if
aparecer na metade do loop, é claro que deve existir como um teste separado. No entanto, se esse teste aparecer no final do bloco, você criou um loop que possui um teste contínuo nas duas extremidades do loop, possivelmente aumentando a complexidade do código. Esteja ciente de que, às vezes, oif (condition)
teste só faz sentido após a execução do bloco.Mas supondo que não seja esse o caso, adotando a outra abordagem:
as condições de saída são mantidas juntas, o que pode simplificar as coisas (especialmente se " algum outro código " for longo).
Não há resposta certa aqui, é claro. Portanto, esteja ciente das expressões idiomáticas da linguagem, quais são as convenções de codificação de sua equipe etc.
fonte
Esse tipo de pergunta provocou debates quase tanto quanto a programação. Para jogar meu chapéu no ringue, eu iria para a versão com a
break
condição e aqui está o porquê (neste caso específico ):O
for
loop está lá apenas para especificar os valores de iteração no loop. Codificar a condição de interrupção dentro da qual apenas atrapalha a lógica IMO.Ter um separador
break
deixa claro que, durante o processamento normal, os valores são os especificados nofor
loop e o processamento deve continuar, exceto onde a condição entrar.Como pano de fundo, comecei a codificar a
break
, depois coloquei a condição nofor
loop por anos (sob a direção de um programador excepcional) e depois voltei aobreak
estilo porque parecia muito mais limpo (e era o estilo predominante em os vários volumes de código que eu estava consumindo).É outra daquelas guerras sagradas no final do dia - alguns dizem que você nunca deve sair artificialmente de um loop, caso contrário, não é um loop real. Faça o que achar que é legível (tanto para você quanto para os outros). Se reduzir o pressionamento de teclas é realmente o seu lugar, vá jogar código no fim de semana - cerveja opcional.
fonte
for
loops são usados quando o número de iterações é conhecido (por exemplo, quando não há condição de interrupção, mas o final da matriz / lista) ewhile
quando não são. Este parece ser o caso de umwhile
loop. Se a leitura é a preocupação deidx+=1
dentro do loop é muito mais limpo para ler do que umif/else
blocofor(int i=0; i<something;i++)
loops que acho que metade dos desenvolvedores realmente não se lembra que osfor
loops podem ser usados de maneira diferente e, portanto, têm dificuldade em entender a&&condition
parte.for(;;)
...for
loops são para iteração sobre algo 1 - eles não são apenas lol-vamos-puxar-algumas-coisas-aleatórias-do-corpo-e-colocá-los-no-loop-cabeçalho - as três expressões têm muito significados específicos:A idéia é separar o tratamento da iteração ( como iterar) da lógica dentro do loop ( o que fazer em cada iteração). Geralmente, muitos idiomas têm um loop for-each que libera você dos detalhes da iteração, mas mesmo que seu idioma não tenha essa construção ou se não puder ser usado no seu cenário - você ainda deve limitar o cabeçalho do loop para manipulação de iteração.
Então, você precisa se perguntar - sua condição é sobre o tratamento da iteração ou sobre a lógica dentro do loop? As chances são de que se trata da lógica dentro do loop - portanto, ele deve ser verificado dentro do loop e não no cabeçalho.
1 Ao contrário de outros loops, que estão "esperando" que algo aconteça. Um loop
for
/foreach
deve ter o conceito de uma "lista" de itens para iterar - mesmo que essa lista seja lenta, infinita ou abstrata.fonte
someFunction(array[i])
. Agora, ambas as partes da condição são sobre o assunto sobre o qual você está iterando e faria sentido combiná-las&&
no cabeçalho do loop.for
loops no coração são contadores, não loops em uma lista, e isso é suportado pela sintaxe defor
idiomas anteriores (esses idiomas geralmente tinham umafor i = 1 to 10 step 2
sintaxe e ostep
comando - mesmo permitindo um valor inicial que não é o índice do primeiro elemento - sugere um contexto numérico, não um contexto de lista). O único caso de uso que eu acho que suportafor
loops apenas como iterativo em uma lista é paralelo, e isso requer sintaxe explícita agora de qualquer maneira para não quebrar o código, por exemplo, uma classificação.Eu recomendo usar a versão com
if (condition)
. É mais legível e mais fácil de depurar. Escrever três linhas extras de código não quebrará os dedos, mas facilitará a compreensão da sua próxima pessoa.fonte
naming things
correto , mas sinto falta de algo como corretamente para entender o que está acontecendo e quando a condição de quebra é atendida.Se você estiver repetindo uma coleção em que certos elementos devem ser ignorados, recomendo uma nova opção:
Java e C # tornam isso relativamente trivial. Não ficaria surpreso se o C ++ tivesse uma maneira de criar um iterador sofisticado para pular certos elementos que não se aplicam. Mas isso só é realmente uma opção se a sua linguagem de escolha a suportar, e a razão pela qual você está usando
break
é porque existem condições para processar ou não um elemento.Caso contrário, há uma boa razão para tornar sua condição parte do loop for - supondo que a avaliação inicial esteja correta.
Eu trabalhei no código em que seu loop for leva algumas centenas de linhas com vários condicionais e algumas matemáticas complexas acontecendo lá. Os problemas com os quais você se depara são:
break
comandos escondidos na lógica são difíceis de encontrar e às vezes surpreendentesNessa situação, recomendo as seguintes diretrizes em ordem de preferência:
break
se possívelbreak
no topo do loop, se possívelEssas diretrizes estão sempre sujeitas à correção do seu código. Escolha a opção que melhor possa representar a intenção e melhorar a clareza do seu código.
fonte
continue
instruções, mas não vejo como elas ajudam muitobreak
.takeWhile
filtro pode substituirbreak
instruções. Se você tiver um - Java, por exemplo, só recebe-los em Jave 9 (não que é que difícil de implementar it yourself)Eu recomendo fortemente adotar a abordagem da menor surpresa, a menos que você obtenha um benefício significativo fazendo o contrário.
As pessoas não leem todas as letras ao ler uma palavra e não lêem todas as palavras ao ler um livro - se são adeptos da leitura, olham os contornos das palavras e frases e deixam o cérebro preencher o resto .
Portanto, é provável que um desenvolvedor ocasional assuma que isso é apenas um padrão para loop e nem sequer veja:
Se você deseja usar esse estilo de qualquer maneira, recomendo alterar as partes
for(int i=0;i<
e;i++)
informar ao cérebro do leitor que esse é um padrão para loop.Outro motivo para seguir com
if
-break
é que você nem sempre pode usar sua abordagem com afor
condição -. Você precisa usarif
-break
quando a condição de interrupção é muito complexa para se esconder dentro de umafor
instrução ou depende de variáveis acessíveis apenas dentro dofor
loop.Então, se você decidir ir com
if
-break
, estará coberto. Mas se você optar por usarfor
-conditions, precisará usar uma combinação deif
-break
efor
-conditions. Para ser consistente, você precisará mover as condições para frente e para trás entre asfor
condições e oif
-break
sempre que as condições mudarem.fonte
Sua transformação está assumindo que tudo o que
condition
é avaliado étrue
quando você entra no loop. Não podemos comentar sobre a correção disso neste caso, porque não está definido.Tendo conseguido "reduzir linhas de código" aqui, você pode então continuar olhando
e aplique a mesma transformação, chegando a
O que é uma transformação inválida, como você agora faz incondicionalmente
further code
onde não fazia anteriormente.Um esquema de transformação muito mais seguro é extrair
some other code
e avaliarcondition
para uma função separadafonte
TL; DR Faça o que for melhor em sua circunstância específica
Vamos pegar este exemplo:
Nesse caso, não queremos que nenhum código do loop for
something
seja executado se for verdadeiro, portanto, mover o teste desomething
para o campo de condição é razoável.Então, novamente, dependendo de se
something
pode ser definido comotrue
antes do loop, mas não dentro dele, isso pode oferecer mais clareza:Agora imagine isso:
Estamos enviando uma mensagem muito clara lá. Se
something
for verdadeiro durante o processamento da matriz, abandone toda a operação neste momento. Para mover a verificação para o campo de condição, você precisa:Indiscutivelmente, a "mensagem" ficou turva, e estamos verificando a condição de falha em dois lugares - e se a condição de falha mudar e esquecermos um desses lugares?
É claro que existem muitas outras formas diferentes que um loop e uma condição poderiam ser, todas com suas próprias nuances.
Escreva o código para que ele leia bem (isso é obviamente um pouco subjetivo) e concisa. Fora de um conjunto draconiano de diretrizes de codificação, todas as "regras" têm exceções. Escreva o que você acha que expressa melhor a tomada de decisão e minimize as chances de erros futuros.
fonte
Embora possa ser atraente porque você vê todas as condições no cabeçalho do loop e
break
pode ser confuso dentro de blocos grandes, umfor
loop é um padrão no qual a maioria dos programadores espera loop em algum intervalo.Especialmente desde que você faz isso, adicionar uma condição de interrupção adicional pode causar confusão e é mais difícil ver se é funcionalmente equivalente.
Geralmente, uma boa alternativa é usar um loop
while
oudo {} while
que verifica as duas condições, assumindo que sua matriz tenha pelo menos um elemento.Usando um loop que apenas verifica uma condição e não executa o código no cabeçalho do loop, você deixa muito claro quando a condição é verificada e qual é a condição real e qual é o código com efeitos colaterais.
fonte
for(...; i <= n; ...)
) torna o código mais difícil de ler porque eu tenho parar e pensar no que está acontecendo aqui e pode perder completamente a condição na primeira passagem, porque não espero que ela esteja lá. Para mim, umfor
loop implica que você está fazendo um loop em algum intervalo; se houver uma condição de saída especial, ele deve ser explicitado com umif
, ou você pode usar umwhile
.Isso é ruim, pois o código deve ser lido pelos seres humanos -
for
loops não-idiomáticos geralmente são confusos de ler, o que significa que os bugs provavelmente estão escondidos aqui, mas o código original pode ter sido possível melhorar, mantendo os dois curto e legível.Se o que você deseja é encontrar um valor em uma matriz (a amostra de código fornecida é um pouco genérica, mas também pode ser esse padrão), tente explicitamente apenas usar uma função fornecida por uma linguagem de programação especificamente para esse fim. Por exemplo (pseudocódigo, como uma linguagem de programação não foi especificada, as soluções variam dependendo de uma linguagem específica).
Isso deve reduzir visivelmente a solução, simplificando-a, pois seu código diz especificamente o que você precisa.
fonte
Na minha opinião, poucas razões dizem que você não deveria.
do...while
loop (nãowhile
), pois a condição é verificada após alguma execução do código.Mas, além disso, considere isso,
Agora, você pode realmente alcançá-lo adicionando essas condições a essa
for
condição?fonte
Eu acho que remover o
break
pode ser uma boa idéia, mas não salvar linhas de código.A vantagem de escrevê-lo sem
break
é que a pós-condição do loop é óbvia e explícita. Quando você termina o loop e executa a próxima linha do programa, sua condição do loopi < array.length && !condition
deve ter sido falsa. Portanto,i
era maior ou igual ao comprimento da matriz oucondition
é verdadeiro.Na versão com
break
, há uma pegadinha oculta. A condição do loop diz que o loop termina quando você executa outro código para cada índice de matriz válido, mas na verdade existe umabreak
instrução que poderia terminar o loop antes disso e você não a verá a menos que revise o código do loop muito cuidado. E os analisadores estáticos automatizados, incluindo a otimização de compiladores, também podem ter problemas para deduzir quais são as pós-condições do loop.Essa foi a razão original pela qual Edgar Dijkstra escreveu “Goto Considerado Prejudicial”. Não era uma questão de estilo: sair de um círculo dificulta a formalidade de raciocinar sobre o estado do programa. Escrevi muitas
break;
declarações em minha vida e, uma vez adulto, até escrevi umgoto
(para romper com vários níveis de um loop interno crítico para o desempenho e continuar com outro ramo da árvore de pesquisa). No entanto, tenho muito mais probabilidade de escrever umareturn
declaração condicional a partir de um loop (que nunca executa o código após o loop e, portanto, não pode estragar suas pós-condições) do que abreak
.Postscript
De fato, refatorar o código para fornecer o mesmo comportamento pode realmente precisar de mais linhas de código. Sua refatoração pode ser válida para algum outro código específico ou se pudermos provar que
condition
isso começará falso. Mas seu exemplo é equivalente no caso geral a:Se algum outro código não for de uma linha, você poderá movê-lo para uma função para não se repetir e quase refatorar seu loop em uma função recursiva de cauda.
Eu reconheço que esse não foi o ponto principal da sua pergunta e você estava mantendo a simplicidade.
fonte
break
declaração, então você sabe qual é a condição do loop e que o loop pára quando essa condição é falsa. Se houver umbreak
? Depois, existem várias maneiras pelas quais o loop pode terminar e, no caso geral, descobrir quando um deles pode interromper o loop está literalmente resolvendo o Problema da Parada. Na prática, minha maior preocupação é que o loop pareça fazer uma coisa, mas, na verdade, quem quer que tenha escrito o código após o loop ignorou um caso extremo escondido em sua lógica complexa. É difícil perder coisas na condição de loop.Sim, mas sua sintaxe não é convencional e, portanto, é ruim (tm)
Espero que seu idioma suporte algo como
fonte
while
enfatiza o monstro no código - a exceção oculta no código rotineiro / mundano. A lógica de negócios é frontal e central, a mecânica de loop é subsumida. Evita a reação "espere um minuto ..." àfor
alternativa do loop. Esta resposta corrige o problema. absolutamente voto.Se você está preocupado com a
for
dificuldade de ler uma afirmação excessivamente complexa , essa é definitivamente uma preocupação válida. Perder espaço vertical também é um problema (embora menos crítico).(Pessoalmente, não gosto da regra de que as
if
declarações sempre devem ter chaves, o que é em grande parte a causa do espaço vertical desperdiçado aqui. Observe que o problema está desperdiçando espaço vertical. Usar o espaço vertical com linhas significativas não deve ser um problema.)Uma opção é dividi-lo em várias linhas. Definitivamente, é isso que você deve fazer se estiver usando
for
instruções realmente complexas , com várias variáveis e condições. (Este é para mim um melhor uso do espaço vertical, dando ao máximo de linhas possível algo significativo.)Uma abordagem melhor pode ser tentar usar uma forma mais declarativa e menos imperativa. Isso variará dependendo do idioma ou talvez você precise escrever suas próprias funções para ativar esse tipo de coisa. Também depende do que
condition
realmente é. Presumivelmente, ele deve ser capaz de mudar de alguma forma, dependendo do que está na matriz.fonte
for
declaração em várias linhas é a melhor idéia em toda esta discussãoUma coisa que foi esquecida: quando interrompo um loop (não faça todas as iterações possíveis, não importa como implementadas), geralmente é o caso de não apenas querer sair do loop, mas também quero saber por que isso aconteceu . Por exemplo, se eu procurar um item X, não apenas rompo o loop, também quero saber que X foi encontrado e onde está.
Nessa situação, ter uma condição fora do loop é bastante natural. Então eu começo com
e no loop vou escrever
com o loop for
Agora, verificar a condição no loop é bastante natural. A existência de uma instrução "break" depende da existência de processamento adicional no loop que você deseja evitar. Se algum processamento for necessário e outro não, você pode ter algo como
em algum lugar do seu loop.
fonte