Me deparei com essa pergunta um segundo atrás e estou retirando parte do material: existe um nome para a construção 'break n'?
Essa parece ser uma maneira desnecessariamente complexa para as pessoas terem de instruir o programa a interromper um loop for aninhado duas vezes:
for (i = 0; i < 10; i++) {
bool broken = false;
for (j = 10; j > 0; j--) {
if (j == i) {
broken = true;
break;
}
}
if (broken)
break;
}
Eu sei que os livros didáticos gostam de dizer que as declarações goto são o diabo, e eu não gosto muito delas, mas isso justifica uma exceção à regra?
Estou procurando respostas que lidem com n-aninhados para loops.
NOTA: Se você responder sim, não ou em algum lugar, respostas completamente fechadas não são bem-vindas. Especialmente se a resposta for não, forneça uma boa razão legítima (que não está muito longe dos regulamentos do Stack Exchange).
coding-style
goto
Panzercrisis
fonte
fonte
for (j = 10; j > 0 && !broken; j--)
, não precisará dabreak;
instrução. Se você mover a declaração debroken
para antes do início do loop externo, se isso puder ser escrito comofor (i = 0; i < 10 && !broken; i++)
eu acho que nãobreak;
é necessário.goto
: goto (Referência de C #) - "A instrução goto também é útil para sair de loops profundamente aninhados."goto
é realmente necessário em um comutador C # para passar para outro caso depois de executar qualquer código dentro do primeiro caso. Esse é o único caso que usei goto, no entanto.Respostas:
A aparente necessidade de uma declaração de aceitação surge da escolha de expressões condicionais ruins para os loops .
Você declara que deseja que o loop externo continue por mais tempo
i < 10
e o mais interno continue por tanto tempoj > 0
.Mas, na realidade, não era isso que você queria , você simplesmente não disse aos loops a condição real que queria que eles avaliassem, então você quer resolvê-lo usando break ou goto.
Se você disser aos loops suas verdadeiras intenções, para começar , não há necessidade de pausas ou ir.
fonte
i
no final do loop externo, portanto é importante um curto-circuito no incremento para tornar o código 100% equivalente. Eu não estou dizendo que nada na sua resposta está errada, eu só queria salientar que quebrar o loop imediatamente foi um aspecto importante do código.i
no final do loop externo na pergunta do OP.goto
(tenho certeza de que existem outros melhores), mesmo que ele tenha gerado uma pergunta que parece usá-lo como tal, mas ilustrando a ação básica debreak(n)
linguagens que não ' Não apóie.Nesse caso, você pode refatorar o código em uma rotina separada e depois apenas
return
no loop interno. Isso negaria a necessidade de romper o loop externo:fonte
i
após o final do loop externo. Então, para realmente refletir quei
é o valor importante, aqui oreturn true;
ereturn false;
deve ser tantoreturn i;
.Não, não acho que
goto
seja necessário. Se você escrever assim:Tendo
&&!broken
em ambos os loops, você pode sair dos dois loops quandoi==j
.Para mim, essa abordagem é preferível. As condições de loop são extremamente claros:
i<10 && !broken
.Uma versão de trabalho pode ser vista aqui: http://rextester.com/KFMH48414
Código:
Resultado:
fonte
broken
em seu primeiro exemplo em tudo ? Basta usar a pausa no loop interno. Além disso, se você absolutamente deve usá-lobroken
, por que declará-lo fora dos loops? Apenas declare-o dentro doi
loop. Quanto aowhile
loop, por favor, não use isso. Sinceramente, acho que ele consegue ser ainda menos legível do que a versão goto.broken
é usada porque eu não gosto de usar abreak
instrução (exceto no caso de troca, mas é isso). É declarado fora do loop externo, caso você queira interromper TODOS os loops quandoi==j
. Você está certo, em geral,broken
só precisa ser declarado antes do loop mais externo que ele irá quebrar.i==2
no final do ciclo. A condição de sucesso também deve causar um curto-circuito no incremento para que seja 100% equivalente a abreak 2
.broken = (j == i)
o if e, se a linguagem permitir, colocar a declaração quebrada dentro da inicialização do loop externo. Embora seja difícil dizer essas coisas para este exemplo específico, como o loop inteiro é um não-op (ele não retorna nada e não tem efeitos colaterais) e se faz alguma coisa, possivelmente faz isso dentro do if (embora eu ainda gostebroken = (j ==i); if broken { something(); }
mais pessoalmente)i
após o final do loop externo. Em outras palavras, o único pensamento realmente importante é quando exatamente ele sai do circuito externo.A resposta curta é "depende". Defendo a legibilidade e a compreensão do seu código. O caminho para o goto pode ser mais legível do que ajustar a condição ou vice-versa. Você também pode levar em consideração o princípio menos surpreendente, a diretriz usada na loja / projeto e a consistência da base de código. Por exemplo, ir para o switch ou para tratamento de erros é uma prática comum na base de código do kernel do Linux. Observe também que alguns programadores podem ter problemas para ler seu código (ou criados com o mantra "goto is evil", ou simplesmente não são aprendidos porque o professor pensa assim) e alguns deles podem até "julgá-lo".
De um modo geral, um ir além na mesma função geralmente é aceitável, especialmente se o salto não for muito longo. Seu caso de uso de deixar um loop aninhado é um exemplo conhecido de "não é tão mau".
fonte
No seu caso, goto é uma melhoria suficiente que parece tentadora, mas não é tanto uma melhoria que vale a pena quebrar a regra contra usá-los em sua base de código.
Pense no seu futuro revisor: ele / ela terá que pensar: "Essa pessoa é uma má codificadora ou é de fato um caso em que o goto valeu a pena? Deixe-me levar 5 minutos para decidir isso por conta própria ou pesquisar no Internet." Por que diabos você faria isso com seu futuro codificador quando não pode usar o goto inteiramente?
Em geral, essa mentalidade se aplica a todo código "ruim" e até o define: se funciona agora e é bom nesse caso, mas cria um esforço para verificar se está correto, o que nesta época um goto sempre fará, então não faça.
Dito isto, sim, você produziu um dos casos um pouco mais espinhosos. Você pode:
É muito difícil para mim criar um caso em que um loop duplo não seja tão coeso que não deva ser sua própria rotina.
A propósito, a melhor discussão sobre isso que eu vi é Code Complete, de Steve McConnell . Se você gosta de pensar criticamente nesses problemas, recomendo fortemente a leitura, mesmo de capa a capa, apesar de sua imensidão.
fonte
TLD; DR: Não em específico, sim em geral
Para o exemplo específico que você usou, eu diria não pelas mesmas razões que muitos outros apontaram. Você pode facilmente refatorar o código em um formato equivalente, o que elimina a necessidade de
goto
.Em geral, a proscrição contra
goto
é uma generalidade baseada na natureza compreendidagoto
e no custo de usá-la. Parte da engenharia de software é tomar decisões de implementação. A justificação dessas decisões ocorre ponderando o custo em relação aos benefícios e sempre que os benefícios superam o custo, a implementação é justificada. A controvérsia surge devido a diferentes avaliações, tanto de custo quanto de benefício.O ponto é que sempre haverá exceções em que a
goto
é justificada, mas apenas para algumas por causa de diferentes avaliações. A pena é que muitos engenheiros simplesmente excluam as técnicas por ignorância intencional, porque as leem na internet em vez de dedicar algum tempo para entender o porquê.fonte
Eu não acho que este caso justifique uma exceção, pois isso pode ser escrito como:
fonte