¿En qué punto es tabú tener bucles dentro de los bucles?

23

Sólo curioso. Lo máximo que he tenido fue un bucle for dentro de un bucle for, porque después de leer esto de Linus Torvalds:

Las pestañas tienen 8 caracteres y, por lo tanto, las sangrías también tienen 8 caracteres. Hay movimientos herejes que intentan hacer sangrías con 4 (¡o incluso 2!) Caracteres de profundidad, y eso es similar a tratar de definir el valor de PI como 3.

Justificación: toda la idea detrás de la sangría es definir claramente dónde comienza y termina un bloque de control. Especialmente cuando ha estado mirando su pantalla durante 20 horas seguidas, le resultará mucho más fácil ver cómo funciona la sangría si tiene sangrías grandes.

Ahora, algunas personas afirmarán que tener muescas de 8 caracteres hace que el código se mueva demasiado a la derecha y dificulta la lectura en una pantalla de terminal de 80 caracteres. La respuesta a eso es que si necesita más de 3 niveles de sangría, de todos modos está jodido y debe arreglar su programa.

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

Pensé que era una práctica inaceptable para mí ir a una tercera capa de bucle y reestructuraría mi código (principalmente Qt).

¿Estaba bromeando Linus?

¿Depende del idioma o la aplicación?

¿Hay algunas cosas que necesitan absolutamente tres o más niveles de bucle?

Akiva
fuente
8
Me confundo por qué saltas de sangría a niveles de bucle. Tiene una gran cita sobre sangría y, de repente, a partir de eso sigue una pregunta sobre bucles anidados.
Pieter B
55
Linus probablemente no está (solo) bromeando en esa sección, pero tenga en cuenta que esta es solo una guía de estilo, y la misma guía de estilo enfatiza que "el estilo de codificación del núcleo es súper simple", es decir, más que otros estilos.
55
@ Akiva No puede pasar por una matriz de 4 dimensiones sin tener 4 bucles anidados. Me parece una locura que alguien limite la cantidad de bucles anidados que puede tener. Linus obviamente estaba siendo muy general y no deberías tomar todo lo que lees como escritura sagrada.
Alternatex
99
@Alternatex El hecho de que necesite 4 bucles no significa que tengan que estar anidados léxicamente . Es bastante obvio por la cita que estamos hablando de cómo organizar el código, no de la ejecución.
44
@delnan No estoy diciendo que 4 bucles anidados sean visualmente agradables y sé que hay otras formas de hacerlo, pero me parece tonto cómo OP tomó las palabras de Linus tan literalmente. 4to nivel de sangría = fin del mundo. Dáme un respiro.
Alternatex

Respuestas:

19

El núcleo prefiere fuertemente algoritmos simples

Si bien una variedad de algoritmos puede requerir bucles profundamente anidados dentro de los bucles, en el contexto del kernel de Linux (en el que se dijo la cita) generalmente necesita respuestas rápidas en tiempo real. En ese contexto, el anidamiento profundo es un olor que puede indicar que el flujo de código es demasiado complejo para este dominio y es posible que deba modificarse debido a sus características de ejecución, no a problemas de legibilidad o sangría.

Además, el kernel de Linux es diferente de la mayoría de los códigos de aplicación en cuanto a los requisitos de auditabilidad y prueba, y por lo tanto preferiría no tener un algoritmo anidado de nivel 4+ en una sola función. Debería ser obvio ver lo que cada fragmento de código hace exactamente y en detalle, incluidos todos los posibles casos de flujo de control y borde. El código profundamente anidado dificulta eso.

Pedro es
fuente
Entonces, ¿cree que con lenguajes de nivel inferior como C, los bucles profundamente anidados generalmente son becauseproyectos más tabú que utilizan lenguajes de nivel inferior que se benefician de un estilo de codificación que se centra en algoritmos más simples?
Akiva
44
@ Akiva No lo vincularía con lenguajes de nivel inferior o C como tal, sino más bien con el dominio del código. Creo que se aplicarían pautas similares para cualquier idioma al escribir código que debe ser robusto, centrado en la seguridad y auditable a expensas de otras cosas. Por ejemplo, una biblioteca de cifrado escrita en Java o Haskell también debe escribirse en un estilo que mantenga las cosas lo más simple posible, limite el anidamiento e intente separar todo en fragmentos que puedan analizarse fácilmente con todas sus posibles consecuencias.
Peteris
Un comentario / respuesta muy perspicaz y útil. Sólo curioso; ¿Qué tipo de proyecto realizado hoy que utiliza un lenguaje de bajo nivel no se enfocaría en ser robusto, auditable y seguro?
Akiva
77
@Akiva, por ejemplo, el código de aprendizaje automático donde es posible que desee usar C solo por razones de rendimiento, pero no le importa mucho la solidez o la seguridad, ya que se ejecutará internamente en condiciones controladas. Además, la implementación de una funcionalidad comercial simple en pequeños microcontroladores integrados, en la práctica, a menudo tiene un enfoque comercial como las características y la velocidad de desarrollo a expensas de la calidad y la seguridad, pero utiliza lenguajes de bajo nivel.
Peteris
49

Hasta cierto punto, dejé de tomarme esta cita en serio en "Las pestañas son 8 caracteres" . El objetivo de los tabuladores es que no son un número fijo de caracteres (en todo caso, una pestaña es un carácter). Qué carga de tosh. Del mismo modo, no estoy completamente convencido de que establecer una regla estricta de "tres niveles de sangrado" sea sensato (tanto como establecer una norma estricta para cualquier cosa es sensata).

Sin embargo, limitar sus niveles de sangría es, en general, una sugerencia razonable, y no una que debería sorprenderle.

En última instancia, si su programa necesita tres niveles de iteración, eso es lo que necesita su programa . El espíritu de la cita no es aliviar mágicamente ese requisito de su proyecto, sino desvincular la lógica en funciones y tipos para que su código sea más terso y expresivo.

Esto solo retroalimenta la misma guía dada anteriormente con respecto a los niveles de sangría. Se trata de cómo estructurar su código y mantenerlo legible, mantenible y divertido de modificar en los años venideros.

La ligereza corre con Mónica
fuente
66
Creo que la "declaración" de que las pestañas tienen 8 caracteres están específicamente en el contexto del desarrollo del kernel. Esta cita está tomada de una guía de codificación para un proyecto específico y no pretende ser una guía de uso general, por lo que se espera que sea bastante obstinada.
Lie Ryan
66
@LieRyan: Entonces todavía es inestable: ¡una pauta de codificación para cualquier cosa no tiene nada que dictar qué tan ancho configuro mis pestañas! Pero sospecho que Linus lo sabe.
Lightness compite con Monica el
66
y, por supuesto, depende del lenguaje: en c # es común que sangre dentro de su espacio de nombres, en su clase y en su método ... ya está en 3 niveles de sangría antes de hablar de que los cuerpos de declaración de flujo de control sangrado.
PeterL
3
@LightnessRacesinOrbit Interpreto el comentario "Las pestañas son 8 caracteres" para no significar que debe ver personalmente las pestañas como 8 de ancho en su editor, sino con el propósito de otras reglas en la guía de estilo (como "El límite en la longitud de las líneas es de 80 columnas y este es un límite altamente preferido ".) uno debe tratar las pestañas como si fueran 8 columnas, esto también es relevante para otras reglas con respecto a la alineación de argumentos en las llamadas a funciones. Una vez más, no creo que la intención de esa línea te obligue a ver las pestañas de esa manera, he hecho parches en el kernel antes con 4 pestañas anchas y volví a fluir el código al final.
Vality
44
@underscore_d: Parece que estoy equivocado: Outside of comments, documentation and except in Kconfig, spaces are never used for indentation, and the above example is deliberately broken.- 6 párrafos hacia abajo de la cita en el OP.
slebetman
16

El punto es el mismo que para cualquier construcción de control de flujo: si el código es difícil de entender, debe refactorizarlo. Si está haciendo una manipulación simple de una matriz multidimensional, puede ser apropiado tener bucles anidados de cinco o seis de profundidad, siempre que la lógica en el bucle más interno sea sencilla. Sin embargo, si está procesando una lógica comercial complicada y el cuerpo de su ciclo es de una docena de líneas o más, entonces probablemente no querrá anidar más de un ciclo de profundidad. Puede intentar calcular la complejidad ciclomática del código, pero lo que realmente se reduce a la legibilidad y facilidad de mantenimiento del código en cuestión.

TMN
fuente
11
Exactamente. Es demasiado fácil sugerir que Torvalds es un loco. (Lo es, por supuesto). Puede ser demasiado rígido para su gusto, pero está describiendo una preocupación real por el desarrollo que causa problemas reales. No tiene que hacer exactamente lo que dice, pero debe pensar por qué lo dice.
Escaso Roger
77
@ScantRoger En realidad, esa cita de Torvalds solo suena demasiado rígida si no entiendes su sentido del humor. Como recuerdo, anteriormente en el mismo documento, sugiere imprimir una copia de las pautas de estilo de codificación GNU, solo para grabarlas en algún tipo de ceremonia. Difícilmente te lo tomarás en serio, ¿verdad? En esta cita, su punto principal es definir la sangría para que el kernel de Linux sea ocho espacios, nada más y nada menos, eso es lo que es rígido. La última oración es solo para subrayar ese punto, no para decir que no debe usar más niveles de sangría, sin rigidez implícita.
cmaster
1
@cmaster ¡Gracias por el contexto, de inmediato! En respuesta a su consulta, apenas me tomo nada en serio. ;)
Scant Roger
2
@cmaster y luego uno lee sus respuestas a las solicitudes de extracción de github y la longitud de la línea de los mensajes de confirmación. Él es un loco total.
Gusdor
3
Grabar ceremoniosamente las pautas de codificación GNU puede no ser realmente necesario, pero está completamente en orden en cualquier momento.
dmckee
13

¿Estaba bromeando Linus?

La pieza está escrita en un estilo lúdico que sugiere que el autor está familiarizado con la forma en que se discute el estilo de codificación entre los profesionales serios: todos tenemos nuestras preferencias y las defendemos con rabia, pero con la lengua al menos parcialmente en la mejilla. Entendemos perfectamente que gran parte es solo una cuestión de gusto personal. Él dice, en muchas palabras, "Coding style is very personal, and I won't _force_ my views on anybody"- al menos fuera del código que mantiene personalmente. Pero la coherencia de estilo en un proyecto dado es una muy buena idea. Prefiero codificar a un estilo que no me gusta que tratar con múltiples estilos en una función determinada.

Aquí hay un ejemplo de escritura claramente juguetona:

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).

Juguetón (1).

Podría decirse que es un buen consejo tratar de evitar que la sangría se salga de control, aunque un máximo de tres niveles podría ser hiperbólico. No voy a aprovechar la fuente del núcleo y contar las secuencias de cuatro caracteres de tabulación, pero apuesto dinero a que podrías encontrar al menos uno que Torvalds escribió.

Por otro lado, si alguien puede escribir el kernel de Linux sin exceder a menudo tres niveles de sangría, un límite de tres niveles podría ser un ejercicio que vale la pena probar por un tiempo en su propio código, solo para ver a dónde lo lleva. Esto no es como un cambio de sexo, ya sabes. No es un compromiso de por vida.

Si te encuentras con alguien en Internet que cree que entiende la programación mucho mejor que Torvalds (2), bueno, ya sabes qué tipo de personas les gusta hablar en grande en Internet.

Por otro lado, está criminalmente equivocado acerca de las pestañas de ocho espacios. Ese es el delirio de un hombre que debe mantenerse sujeto y alimentado a través de una ranura. Cuatro espacios es obviamente correcto.

(1) Pero observe cómo coloca erróneamente un espacio antes de las elipses, y dos espacios después de ellas, y dos espacios después de un punto final. Incorrecto, incorrecto, incorrecto. Y luego tiene el descaro descaro de castigar a los herejes. ¡El hereje eres tú, Torvalds! ¡ERES TÚ!

(2) Si desea hablar sobre " comprender cómo diseñar un sistema de control de fuente ", puede haber espacio para el debate.

Nota: Estimado usuario que ha enviado repetidamente la misma edición: el formato en el material citado se mantiene exactamente como el autor lo quiso decir. Eso es porque es de un ensayo sobre el formato de texto de ancho fijo, escrito en texto de ancho fijo, por alguien que le ha dado una buena cantidad de pensamiento al formato. El formato es una parte consciente y deliberada de la intención del autor, y es relevante para el tema.

Además, me referí a ese formato en mi propio texto. Si elimina el preformateo, mi nota al pie (1) se convierte en galimatías. Si se elimina el formateo previo, también debería ser el texto en mi nota al pie de página (1) que se refiere a los pares de espacios después de las paradas completas en los extremos de las oraciones. De todos modos, puedo ver una razón para eliminar esa nota al pie de página, debido a que es menos divertida de lo que parecía cuando la escribí. Pero eliminar el formato sin eliminar la nota al pie no es útil.

Ed Plunkett
fuente
3
Maravillosa respuesta. Uno de los casos que merecería un +2 ... (Nota: no hay espacios incorrectos .en este comentario ;-))
cmaster
2
El párrafo de introducción de Linus que usted señaló es muy importante, ¡así que gracias por hacerlo! Creo que la primera oración también es muy importante para el contexto, específicamente preferred coding styleasí comobut this is what goes for anything that I have to be able to maintain
Chris Haas
9

Linus tiene un estilo de hablar muy directo y un sentido del humor seco, pero no estaba bromeando en este caso. Hay situaciones en las que un algoritmo necesita anidarse más profundo que dos niveles, pero puede lograr esto utilizando otros medios que no sean la sangría de su código. La guía de estilo del kernel de Linux prefiere estos otros métodos, debido a la dificultad de mantener bucles profundamente anidados, y eso es lo que Linus está diciendo aquí.

Para algunos ejemplos de métodos alternativos, puede usar la recursión, dividir los bucles internos en sus propias funciones o crear estructuras de datos intermedias.

El anidamiento excesivo es uno de esos casos que es más fácil de escribir, pero más difícil de leer. Establecer una profundidad de tabulación grande es la forma en que Linus hace que escribir sea más molesto.

Karl Bielefeldt
fuente
3

Hay muchas preguntas en las que el consejo es diferente para alguien que hace la pregunta que para alguien que no pregunta. Si pregunta "Si alguna vez tengo bucles que están anidados más de dos niveles de profundidad", para usted, la persona que hace esa pregunta, la respuesta es NO. Si preguntas, entonces no lo hagas. Si tiene suficiente experiencia que no necesita preguntar, entonces sabe cuál es la respuesta correcta en cada caso. Y no discuta si no está de acuerdo con la respuesta, porque la respuesta no es para usted.

gnasher729
fuente
1

Esto parecería ser un caso de libro de texto de la cola que menea al perro.

Si tiene una pantalla de 80 caracteres, por supuesto , intentará hacer que el código se ajuste de la mejor manera posible, incluso si no produce la mejor estructura para el código .

Abordando el resto de tus puntos de frente:

Supuse que era una práctica inaceptable.

Creo que estás leyendo demasiado sobre esto. Resista el impulso de tomar todo lo que lee como evangelio sin comprender adecuadamente el contexto.

¿Estaba bromeando?

Es difícil determinar el contexto, pero vea mi punto original arriba.

¿Depende del idioma o la aplicación?

Mucho más. Tome cualquier lenguaje de mainframe / rango medio donde sea probable que esté codificando en un terminal (o emulador de terminal).

¿Hay algunas cosas que necesitan absolutamente tres o más niveles de bucle?

Sí, es muy común en algunos algoritmos de fuerza bruta. Vea el problema 31 en el proyecto Euler. Este es un ejemplo clásico de un problema que podría resolverse con fuerza bruta utilizando varios bucles (8 para ser exactos).

Robbie Dee
fuente
1
Parece que el problema 31 no requiere fuerza bruta y podría resolverse usando un algoritmo de programación dinámico (editar: lo que significa que su estructura de código no es la mejor si está usando un algoritmo de fuerza bruta). Además, el punto de Linus es que si su código requiere muchos niveles de sangría, probablemente no sea la mejor estructura para el código.
Vincent Savard
2
@VincentSavard Nunca dije que requería fuerza bruta. No está de acuerdo con su segundo punto: a veces es el enfoque más claro y sucinto, sin mencionar el más eficiente en algunos casos.
Robbie Dee
1
Con ese tipo de problema, generalmente no sangro los bucles. Creo que tenía un caso con 20 bucles anidados, absolutamente trivial para escribir, y sin sangría para que pudiera ver que los bucles eran casi idénticos.
gnasher729
1
@RobbieDee: Mi punto es que su ejemplo de un problema resuelto por muchos bucles es que su algoritmo no es tan eficiente como una solución de programación dinámica, que no requiere tantos niveles de sangría. Por lo tanto, como dijo Linus, sus niveles de sangría pueden eliminarse utilizando una solución mejor. También entendiste mal mi segundo punto porque estoy de acuerdo con lo que dijiste. A veces , es la mejor solución. A veces no es frecuente, y no es probable.
Vincent Savard
1
La cita de Linus dice de manera bastante explícita que si algún código requiere algo así como la fuerza bruta del problema 31, entonces estás jodido de todos modos: no será rápido ni simple, y las operaciones del núcleo deben ser rápidas y simples. La inclusión de cualquier algoritmo O (n ^ 4) en el kernel es un riesgo significativo de problemas de rendimiento o denegación de servicio, por lo que en este contexto la recomendación simplemente advierte que este es un signo de código que puede ser fundamentalmente inapropiado y deseado en Linux.
Peteris
0

¿Estaba bromeando Linus?

No, esas son las pautas oficiales.

¿Depende del idioma o la aplicación?

Las pautas de codificación generalmente dependen del idioma y la aplicación, sin embargo, el código anidado siempre grava al lector.

El problema con el código anidado es que, en general, aumenta la complejidad ciclomática: es decir, cuanto más anidado está el código, más rutas de ejecución potenciales existen dentro de la función. Una explosión combinatoria de posibles rutas de ejecución hace que sea difícil razonar sobre el código y, por lo tanto, debe evitarse en general.

Entonces, ¿por qué 3? Una directriz de codificación subjetiva es difícil de aplicar e imposible de aplicar automáticamente. Establecer una directriz de codificación objetiva en el nivel máximo de sangría requiere acordar un número: en el kernel de Linux eligieron 3.

Es arbitrario, y aparentemente suficiente para ellos.

¿Hay algunas cosas que necesitan absolutamente tres o más niveles de bucle?

Probablemente en algoritmos, sin embargo, en lenguajes suficientemente expresivos siempre puede refactorizar el código en fragmentos más pequeños (ya sea con funciones o cierres).

Obviamente, puede escribir código ofuscado con poco anidamiento y muchas funciones pequeñas que se llaman sin deletrear su contrato ...

... sin embargo, las funciones pequeñas con contratos claros son mucho más fáciles de auditar que las funciones grandes con contratos claros en general.

Matthieu M.
fuente
2
Si bien esta podría ser la directriz oficial, es trivial encontrar lugares en el código del núcleo donde la directriz no se aplique.
MikeB
1
@MikeB: Más razones para hacer cumplir las pautas automáticamente ...
Matthieu M.
1
@MatthieuM. ¿Seguro que comprende la diferencia entre las pautas y los requisitos obligatorios? Como una "regla general" general (una guía si lo desea), las pautas son más como recomendaciones y no se aplican.
Brendan