Em que ponto é tabu ter loops dentro de loops?

23

Apenas curioso. O máximo que eu já tive foi um loop for dentro de um loop for, porque depois de ler isso em Linus Torvalds:

As guias têm 8 caracteres e, portanto, os recuos também têm 8 caracteres. Existem movimentos heréticos que tentam aprofundar os recuos de 4 (ou até 2!) Caracteres, e isso é semelhante a tentar definir o valor de PI como 3.

Fundamentação da petição: A idéia por trás do recuo é definir claramente onde um bloco de controle começa e termina. Especialmente quando você olha para a tela por 20 horas seguidas, é muito mais fácil ver como a indentação funciona se você tiver grandes indentações.

Agora, algumas pessoas alegam que ter recuos de 8 caracteres faz com que o código se mova muito para a direita e dificulta a leitura em uma tela de terminal de 80 caracteres. A resposta para isso é que, se você precisar de mais de 3 níveis de indentação, estará ferrado de qualquer maneira e deverá corrigir seu programa.

https://www.kernel.org/doc/Documentation/CodingStyle

Achei que era uma prática inaceitável para eu ir para uma terceira camada de loop e reestruturar meu código (Principalmente Qt).

Linus estava brincando?

Depende do idioma ou aplicativo?

Existem coisas que absolutamente precisam de três ou mais níveis de loop?

Akiva
fonte
8
Fico confuso por que você pula do recuo para os níveis de loop? Você tem uma grande citação discutindo indentação e, subitamente, segue uma pergunta sobre loops aninhados.
Pieter B
5
O Linus provavelmente não está (apenas) brincando nessa seção, mas observe que este é apenas um guia de estilo, e o mesmo guia enfatiza que "O estilo de codificação do kernel é super simples", ou seja, mais do que outros estilos.
5
@ Akiva Você não pode passar por uma matriz 4-dimensional sem ter 4 loops aninhados. Acho insano que alguém limite a quantidade de loops aninhados que você pode ter. Linus estava obviamente sendo muito genérico e você não deve levar tudo que lê como sagrada escritura.
Alternatex
9
@Alternatex O fato de você precisar de 4 loops não significa que eles precisam ser aninhados lexicamente . É bastante óbvio da citação que estamos falando sobre como organizar o código, não sobre a execução.
4
@delnan Não estou dizendo que 4 loops aninhados são visualmente agradáveis ​​e estou ciente de que há outras maneiras de fazer isso, mas acho bobo como o OP considerou as palavras de Linus tão literalmente. 4º nível de indentação = fim do mundo. Me dá um tempo.
Alternatex

Respostas:

19

O kernel prefere fortemente algoritmos simples

Embora uma variedade de algoritmos possa exigir loops profundamente aninhados dentro de loops, no contexto do kernel do Linux (no qual a citação foi mencionada), geralmente você precisa de respostas rápidas em tempo real. Nesse contexto, o aninhamento profundo é um cheiro que pode indicar que o fluxo de código é muito complexo para esse domínio e pode precisar ser alterado devido às suas características de execução, não à legibilidade ou problemas de indentação.

Além disso, o kernel do Linux é diferente da maioria dos códigos de aplicativos quanto aos requisitos de auditabilidade e teste - e, portanto, prefere não ter um algoritmo aninhado de nível 4+ em uma única função. Deve ser óbvio ver o que cada fragmento de código faz exatamente e em detalhes, incluindo todos os casos possíveis de fluxo de controle e borda. Um código profundamente aninhado dificulta isso.

Peter é
fonte
Então, você acha que, com linguagens de nível inferior, como C, loops profundamente aninhados geralmente são mais becauseprojetos tabus , utilizando linguagens de nível inferior, se beneficiam de um estilo de codificação focado em algoritmos mais simples?
Akiva
4
@ Akiva, eu não o vincularia a idiomas de nível inferior ou C, como tal, mas ao domínio do código. Penso que diretrizes semelhantes se aplicariam a qualquer idioma ao escrever código que deva ser robusto, focado na segurança e auditável à custa de outras coisas. Por exemplo, uma biblioteca de criptografia escrita em Java ou Haskell também deve ser escrita em um estilo que mantenha as coisas o mais simples possível, limite o aninhamento e tente separar tudo em partes que possam ser facilmente analisadas com todas as possíveis consequências.
Peteris
Um comentário / resposta muito perspicaz e útil. Apenas curioso; que tipo de projeto feito hoje, que utiliza uma linguagem de baixo nível, não focaria em ser robusto, passível de auditoria e seguro?
Akiva
7
@ Akiva, por exemplo, código de aprendizado de máquina em que você pode usar o C apenas por razões de desempenho, mas não se importa muito com robustez ou segurança, pois ele será executado internamente em condições controladas. Além disso, a implementação de funcionalidades simples de negócios em pequenos microcontroladores incorporados - na prática, isso geralmente tem foco nos recursos e na velocidade de desenvolvimento, em detrimento da qualidade e segurança, mas usa linguagens de baixo nível.
Peteris
49

Até certo ponto, parei de levar a sério essa citação em "Guias de 8 caracteres" . O ponto principal dos tabuladores é que eles não são um número fixo de caracteres (se houver, uma guia é um caractere). Quanta bobagem. Da mesma forma, não estou completamente convencido de que definir uma regra rígida e rápida de "três níveis de indentação" seja sensato (tanto quanto definir uma regra rígida para qualquer coisa é sensato).

No entanto, limitar seus níveis de indentação é geralmente uma sugestão razoável, e não uma que deveria ser uma surpresa para você.

Por fim, se seu programa precisa de três níveis de iteração, é disso que ele precisa . O espírito da citação não é aliviar magicamente esse requisito do seu projeto, mas agrupar a lógica em funções e tipos para que seu código seja mais tenso e mais expressivo.

Isso apenas alimenta a mesma diretriz acima, em relação aos níveis de indentação. É sobre como você estrutura seu código e o mantém legível, fácil de manter e divertido de modificar nos próximos anos.

Corridas de leveza com Monica
fonte
6
Eu acredito que a "declaração" de que as guias têm 8 caracteres está especificamente no contexto do desenvolvimento do kernel. Esta citação é tirada de uma diretriz de codificação para um projeto específico e não se destina a ser uma diretriz de uso geral e, portanto, espera-se que seja bastante opinativa.
Lie Ryan
6
@LieRyan: Então ainda é uma merda - uma diretriz de codificação para qualquer coisa não tem nada a ver com a largura que defino minhas abas! Mas suspeito que Linus sabe disso.
Lightness Races com Monica
6
e é claro que é dependente de idioma - em C # é comum que você recuar dentro do seu espaço de nomes, em sua classe, e em seu método .. você já está em 3 níveis de recuo antes mesmo de falar sobre corpos demonstração dos fluxos de controle que está sendo recuado.
Peterl
3
@LightnessRacesinOrbit Interpreto o comentário "As guias têm 8 caracteres" para não significar que você deve visualizar pessoalmente as guias com 8 de largura no seu editor, mas para o objetivo de outras regras do guia de estilo (como "O limite do comprimento das linhas é 80 colunas e esse é um limite fortemente preferido. ") é necessário tratar as guias como sendo 8 colunas, isso também é relevante para outras regras relacionadas ao alinhamento de argumentos nas chamadas de função. Novamente, eu não acho que a intenção dessa linha esteja forçando você a visualizar as guias dessa maneira, eu já fiz o patch do kernel antes com 4 abas largas e refiz o código no final.
Vality 23/12
4
@underscore_d: Parece que estou errado: Outside of comments, documentation and except in Kconfig, spaces are never used for indentation, and the above example is deliberately broken.- 6 parágrafos abaixo da citação no OP.
slebetman
16

O ponto é o mesmo de qualquer construção de controle de fluxo: se o código for difícil de entender, você precisará refatorá-lo. Se você estiver fazendo uma manipulação simples de uma matriz multidimensional, ter loops aninhados com profundidade de cinco ou seis profundidade pode ser apropriado, desde que a lógica no loop mais interno seja direta. No entanto, se você estiver processando alguma lógica comercial complicada e o corpo do seu loop tiver uma dúzia de linhas ou mais, provavelmente não desejará aninhar mais do que um loop com profundidade. Você pode tentar calcular a complexidade ciclomática do código, mas o que realmente se resume é a legibilidade e manutenção do código em questão.

TMN
fonte
11
Exatamente. É muito fácil sugerir que Torvalds é um maluco. (Ele é, é claro.) Ele pode ser rígido demais para o seu gosto, mas está descrevendo uma preocupação real de desenvolvimento que causa problemas reais. Você não precisa fazer exatamente o que ele diz, mas deve pensar por que ele está dizendo isso.
Scant Roger
7
@ScantRoger Na verdade, essa citação de Torvalds só parece rígida demais se você não entender o humor dele. Pelo que me lembro, anteriormente no mesmo documento, ele sugere imprimir uma cópia das diretrizes de estilo de codificação GNU, apenas para queimá-las em algum tipo de cerimônia. Você dificilmente vai levar isso a sério, não é? Nesta citação, seu ponto principal é definir o recuo para o kernel do Linux como oito espaços, nada mais e nada menos, é sobre isso que ele é rígido. A última frase é apenas para sublinhar esse ponto, para não dizer que você não deve usar mais níveis de indentação - nenhuma rigidez implícita.
C23
1
@master Obrigado pelo contexto, logo! Em resposta à sua pergunta, dificilmente levo algo a sério. ;)
Scant Roger
2
@cmaster e, em seguida, lê-se suas respostas às solicitações pull do github e ao comprimento da linha das mensagens de confirmação. Ele é um maluco total.
Gusdor
3
A gravação cerimonial das diretrizes de codificação GNU pode não ser realmente necessária, mas está totalmente em ordem a qualquer momento.
dmckee
13

Linus estava brincando?

A peça é escrita em um estilo lúdico que sugere que o autor esteja familiarizado com o modo como o estilo de codificação é discutido entre os praticantes sérios: todos nós temos nossas preferências e as defendemos raivosamente, mas com a língua pelo menos parcialmente na bochecha. Entendemos perfeitamente que muito disso é apenas uma questão de gosto pessoal. Ele diz, em tantas palavras,"Coding style is very personal, and I won't _force_ my views on anybody" - pelo menos fora do código que ele pessoalmente mantém. Mas a consistência do estilo em um determinado projeto é uma ótima idéia. Prefiro codificar para um estilo que não gosto do que lidar com vários estilos em uma determinada função.

Aqui está um exemplo de escrita claramente divertida:

However, there is one special case, namely functions: they have the
opening brace at the beginning of the next line, thus:

int function(int x)
{
    body of function
}

Heretic people all over the world have claimed that this inconsistency
is ...  well ...  inconsistent, but all right-thinking people know that
(a) K&R are _right_ and (b) K&R are right.  Besides, functions are
special anyway (you can't nest them in C).

Brincalhão (1).

É sem dúvida um bom conselho tentar manter o recuo fora de controle, embora um máximo de três níveis possa ser hiperbólico. Não vou dar uma grep na fonte do kernel e contar sequências de quatro caracteres de tabulação, mas aposto que você poderia encontrar pelo menos um que Torvalds escreveu.

Por outro lado, se alguém pode escrever o kernel do Linux sem exceder frequentemente três níveis de indentação, um limite de três níveis pode ser um exercício que vale a pena experimentar por um tempo em seu próprio código, apenas para ver aonde ele o leva. Isso não é como uma mudança de sexo, você sabe. Não é um compromisso vitalício.

Se você se deparar com alguém na Internet que acha que ele entende de programação muito melhor do que Torvalds (2), bem, você sabe que tipo de pessoa gosta de falar muito na Internet.

Por outro lado, ele está criminalmente errado quanto às guias de oito espaços. Esse é o delírio de um homem que deve ser mantido em restrições e alimentado por uma fenda. Quatro espaços estão obviamente corretos.

(1) Mas observe como ele coloca erroneamente um espaço antes das elipses, e dois espaços depois deles e dois espaços depois de um ponto final. ERRADO, ERRADO, ERRADO. E então ele tem a ousadia descarada de castigar hereges. O herege é você, Torvalds! É VOCÊ!

(2) Se você quiser falar sobre " entender como projetar um sistema de controle de origem ", pode haver algum espaço para debate.

Nota: Caro colega usuário que enviou repetidamente a mesma edição: A formatação no material citado é mantida exatamente como o autor pretendia. Isso ocorre a partir de um ensaio sobre a formatação de texto de largura fixa, escrito em texto de largura fixa, por alguém que pensou bastante na formatação de texto de largura fixa. A formatação é uma parte consciente e deliberada da intenção do autor, e é relevante para o assunto.

Além disso, me referi a essa formatação em meu próprio texto. Se você fizer a pré-formatação, minha nota de rodapé (1) fica sem sentido. Se a pré-formatação for removida, o mesmo deverá constar no texto da nota de rodapé (1), referente aos pares de espaços após as paradas completas no final das frases. De qualquer maneira, vejo uma justificativa para remover essa nota de rodapé, por ser menos engraçada do que parecia quando a escrevi. Mas remover a formatação sem remover a nota de rodapé é inútil.

Ed Plunkett
fonte
3
Resposta maravilhosa. Um dos casos que merecem uma +2 ... (Nota: Sem espaços erradas em torno .deste comentário ;-))
cmaster
2
O parágrafo de introdução de Linus que você apontou é muito importante, por isso obrigado por fazer isso! Eu acho que a primeira frase também é muito importante para o contexto, especificamente preferred coding stylee tambémbut this is what goes for anything that I have to be able to maintain
Chris Haas
9

Linus tem um estilo de falar muito franco e um senso de humor seco, mas ele não estava brincando nesse caso. Há situações em que um algoritmo precisa de um aninhamento mais profundo do que dois níveis, mas você pode fazer isso usando outros meios além do recuo do código. O guia de estilo do kernel do Linux prefere fortemente esses outros métodos, devido à dificuldade de manter loops profundamente aninhados, e é isso que Linus está dizendo aqui.

Para alguns exemplos de métodos alternativos, você pode usar a recursão, dividir os loops internos em suas próprias funções ou criar estruturas de dados intermediárias.

Aninhamento excessivo é um daqueles casos mais fáceis de escrever, mas mais difíceis de ler. Definir uma grande profundidade de tabulação é a maneira de Linus também tornar mais irritante escrever.

Karl Bielefeldt
fonte
3

Há muitas perguntas em que o conselho é diferente para alguém que faz a pergunta e para alguém que não faz. Se você perguntar "Devo ter loops aninhados com mais de dois níveis de profundidade", então para você, a pessoa que faz essa pergunta, a resposta é NÃO. Se você perguntar, não faça. Se você tem experiência suficiente e não precisa perguntar, sabe qual é a resposta correta em cada caso. E não discuta se você não concorda com a resposta, porque a resposta não é para você.

gnasher729
fonte
1

Parece um caso de rabo balançando o cachorro.

Se você tiver uma exibição de 80 caracteres, é claro que tentará ajustar o código da melhor forma possível, mesmo que não produza a melhor estrutura para o código .

Enfrentando o restante dos seus pontos de frente:

Achei que era uma prática inaceitável.

Eu acho que você está lendo muito sobre isso. Resista ao desejo de aceitar tudo que você lê como evangelho sem entender adequadamente o contexto.

Ele estava brincando?

Difícil de determinar o contexto, mas veja meu ponto original acima.

Depende do idioma ou aplicativo?

Muito mesmo. Pegue qualquer idioma de mainframe / midrange em que você provavelmente esteja codificando em um terminal (ou emulador de terminal).

Existem coisas que absolutamente precisam de três ou mais níveis de loop?

Sim, é muito comum em alguns algoritmos de força bruta. Consulte o Problema 31 no Projeto Euler. Este é um exemplo clássico de um problema que pode ser resolvido com força bruta usando um número de loops (8 para ser exato).

Robbie Dee
fonte
1
Parece que o Problema 31 não requer força bruta e pode ser resolvido usando um algoritmo de programação dinâmica (editar: o que significa que sua estrutura de código não é a melhor se você estiver usando um algoritmo bruteforce). Além disso, o argumento de Linus é que, se o seu código exigir muitos níveis de indentação, provavelmente não é a melhor estrutura para o código.
Vincent Savard
2
@VincentSavard Nunca disse que exigia força bruta. Discordo do seu segundo ponto - às vezes é a abordagem mais clara e sucinta, sem mencionar a mais eficiente em alguns casos.
Robbie Dee
1
Com esse tipo de problema, geralmente não recuo os loops. Acho que tive um caso com 20 loops aninhados, absolutamente trivial para escrever e sem recuo para que você pudesse ver que os loops eram quase idênticos.
gnasher729
1
@RobbieDee: Meu argumento é que seu exemplo de um problema resolvido por muitos loops é que seu algoritmo não é tão eficiente quanto uma solução de programação dinâmica, que não exige tantos níveis de indentação. Assim, como Linus disse, seus níveis de indentação podem ser removidos usando uma solução melhor. Você também entendeu mal o meu segundo ponto, porque eu concordo com o que você disse. Às vezes , é a melhor solução. Às vezes não é frequente e não é provável.
Vincent Savard
1
A citação de Linus diz explicitamente que, se algum código exigir algo como forçar brutalmente o Problem-31, você estará ferrado de qualquer maneira - não será rápido nem simples, e as operações do kernel devem ser rápidas e simples. A inclusão de qualquer algoritmo O (n ^ 4) no kernel é um risco significativo de problemas de desempenho ou negação de serviço; portanto, neste contexto, a recomendação simplesmente alerta que este é um sinal de código que pode ser fundamentalmente inadequado e desejado no Linux.
Peteris
0

Linus estava brincando?

Não, essas são as diretrizes oficiais.

Depende do idioma ou aplicativo?

As diretrizes de codificação geralmente dependem do idioma e do aplicativo; no entanto, códigos profundamente aninhados sempre sobrecarregam o leitor.

O problema com o código aninhado é que, em geral, aumenta a complexidade ciclomática: ou seja, quanto mais aninhado o código, mais caminhos de execução em potencial existem na função. Uma explosão combinatória de possíveis caminhos de execução dificulta o raciocínio sobre o código e, portanto, deve ser evitada em geral.

Então, por que 3? Uma diretriz subjetiva de codificação é difícil de aplicar e impossível de aplicar automaticamente. A configuração de uma diretriz objetiva de codificação para o nível máximo de indentação requer a concordância de um número: no kernel do Linux, eles escolheram 3.

É arbitrário e aparentemente suficiente para eles.

Existem coisas que absolutamente precisam de três ou mais níveis de loop?

Em termos de algoritmo, provavelmente, no entanto, em linguagens suficientemente expressivas, você sempre pode refatorar o código em blocos menores (seja com funções ou fechamentos).

Obviamente, você pode escrever código ofuscado com pouco aninhamento e muitas pequenas funções se chamando sem nunca soletrar seu contrato ...

... no entanto, funções pequenas com contratos claros são muito mais fáceis de auditar do que funções grandes com contratos claros em geral.

Matthieu M.
fonte
2
Embora essa possa ser a diretriz oficial, é trivial encontrar lugares no código do kernel onde a diretriz não é aplicada.
MikeB
1
@MikeB: todas as razões para impor diretrizes automaticamente ...
Matthieu M.
1
@MatthieuM. Tem certeza de que entende a diferença entre diretrizes e requisitos obrigatórios? Como "regra geral" geral (uma diretriz, se você preferir), as diretrizes são mais parecidas com as recomendações e não são aplicadas.
Brendan