A che punto è tabù avere dei loop nei loop?


23

Solo curioso. Il massimo che abbia mai avuto è stato un ciclo for all'interno di un ciclo for, perché dopo aver letto questo da Linus Torvalds:

Le schede sono 8 caratteri e quindi anche le rientranze sono 8 caratteri. Ci sono movimenti eretici che tentano di rendere profondi i rientri di 4 (o anche 2!) Caratteri, e questo è simile al tentativo di definire il valore di PI come 3.

Motivazione: L'idea alla base del rientro è quella di definire chiaramente dove inizia e finisce un blocco di controllo. Soprattutto quando guardi il tuo schermo per 20 ore di fila, troverai molto più facile vedere come funziona il rientro se hai grandi rientri.

Ora, alcune persone affermeranno che avere rientri di 8 caratteri fa spostare il codice troppo a destra e rende difficile la lettura su uno schermo terminale di 80 caratteri. La risposta è che se hai bisogno di più di 3 livelli di rientro, sei comunque fregato e dovresti sistemare il tuo programma.

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

Ho pensato che fosse una pratica inaccettabile per me passare a un terzo livello di looping e ristrutturare il mio codice (principalmente Qt).

Linus stava scherzando?

Dipende dalla lingua o dall'applicazione?

Ci sono alcune cose che hanno assolutamente bisogno di tre o più livelli di looping?


8
Mi confondo perché passi dall'indentazione ai livelli di looping? Hai una grande citazione che parla di rientro e improvvisamente da ciò segue una domanda sui cicli nidificati.
Pieter B,

5
Linus probabilmente non scherza (solo) in quella sezione, ma nota che questa è solo una guida di stile, e la stessa guida di stile sottolinea che "Lo stile di codifica del kernel è super semplice", vale a dire più di altri stili.

5
@Akiva Non puoi passare attraverso una matrice 4-dimensionale senza avere 4 anelli nidificati. Trovo folle che qualcuno limiti la quantità di loop nidificati che puoi avere. Ovviamente Linus era molto generico e non dovresti prendere tutto ciò che leggi come sacra Scrittura.
Alternatex,

9
@Alternatex La necessità di 4 loop non significa che debbano essere nidificati in modo lessicale . È abbastanza ovvio dalla citazione che stiamo parlando di come organizzare il codice, non dell'esecuzione.

4
@delnan Non sto dicendo che 4 loop nidificati siano visivamente gradevoli e sono consapevole che ci sono altri modi per farlo, ma trovo sciocco il modo in cui OP ha preso le parole di Linus così letteralmente. 4 ° livello di rientro = fine del mondo. Dammi una pausa.
Alternatex,

Risposte:


19

Il kernel preferisce fortemente algoritmi semplici

Mentre una varietà di algoritmi può richiedere loop profondamente annidati all'interno di loop, nel contesto del kernel Linux (in cui è stata detta la citazione) in genere sono necessarie risposte rapide in tempo reale. In tale contesto, l'annidamento profondo è un odore che potrebbe indicare che il flusso di codice è troppo complesso per questo dominio e potrebbe essere necessario modificarlo a causa delle sue caratteristiche di esecuzione, non di problemi di leggibilità o di rientro.

Inoltre, il kernel Linux è diverso dalla maggior parte del codice dell'applicazione per quanto riguarda i requisiti di audibilità e test - e quindi preferirebbe non avere un algoritmo nidificato di livello 4+ in una singola funzione. Dovrebbe essere ovvio vedere cosa fa esattamente e in dettaglio ogni frammento di codice , compresi tutti i possibili flussi di controllo e casi limite. Il codice profondamente annidato lo ostacola.


Quindi pensi che con linguaggi di livello inferiore come C, i cicli profondamente annidati sono generalmente più becauseprogetti tabù che utilizzano linguaggi di livello inferiore beneficiano di uno stile di codifica che si concentra su algoritmi più semplici?
Akiva,

4
@Akiva Non lo legherei a linguaggi di livello inferiore o C come tale, ma piuttosto al dominio del codice. Penso che linee guida simili si applicherebbero a qualsiasi lingua quando si scrive codice che deve essere solido, incentrato sulla sicurezza e verificabile a spese di altre cose. Ad esempio, una libreria di crittografia scritta in Java o Haskell dovrebbe anche essere scritta in uno stile che mantenga le cose il più semplice possibile, limiti l'annidamento e cerchi di separare tutto in blocchi che possono essere facilmente analizzati con tutte le loro possibili conseguenze.
Peteris,

Un commento / risposta molto perspicace e utile. Solo curioso; che tipo di progetto fatto oggi che utilizza un linguaggio di basso livello, non si concentrerebbe sull'essere solido, capace di controllare e sicuro?
Akiva,

7
@Akiva, ad esempio, codice di apprendimento automatico in cui potresti voler usare C solo per motivi di prestazioni, ma non ti interessa molto della robustezza o della sicurezza poiché verrà eseguito internamente in condizioni controllate. Inoltre, implementando semplici funzionalità aziendali su piccoli microcontrollori incorporati, in pratica questo spesso ha come focus su funzionalità e velocità di sviluppo a scapito della qualità e della sicurezza, ma utilizza linguaggi di basso livello.
Peteris,

49

In una certa misura, ho smesso di prendere sul serio questa citazione in "Le schede sono 8 caratteri" . Il punto centrale dei tabulatori è che non sono un numero fisso di caratteri (semmai, una scheda è un carattere). Che carico di tosh. Allo stesso modo, non sono del tutto convinto che l'impostazione di una regola rigida di "tre livelli di rientro" sia sana (tanto quanto l'impostazione di una regola rigida per qualsiasi cosa sia sana).

Tuttavia, limitare i livelli di rientro è in genere un suggerimento ragionevole, e non dovrebbe essere una sorpresa per te.

In definitiva, se il tuo programma necessita di tre livelli di iterazione, questo è ciò di cui il tuo programma ha bisogno . Lo spirito della citazione non è quello di alleviare magicamente quel requisito dal tuo progetto, ma di abolire la logica in funzioni e tipi in modo che il tuo codice sia più terso e più espressivo.

Ciò si rifà semplicemente alle stesse linee guida fornite sopra per quanto riguarda i livelli di rientro. Si tratta di come strutturare il codice e mantenerlo leggibile, gestibile e divertente da modificare negli anni a venire.


6
Credo che la "dichiarazione" che le schede sono 8 caratteri siano specificamente nel contesto dello sviluppo del kernel. Questa citazione è presa da una linea guida di codifica per un progetto specifico e non è intesa come una linea guida di uso generale, e quindi dovrebbe essere abbastanza supponente.
Lie Ryan,

6
@LieRyan: Quindi è ancora tosh - una linea guida per la codifica di qualsiasi cosa non ha affari a dettare quanto largo impostare le mie schede! Ma sospetto che Linus lo sappia.
Corse di leggerezza con Monica il

6
e ovviamente dipende dalla lingua - in c # è comune che rientri nel tuo spazio dei nomi, nella tua classe e nel tuo metodo .. sei già a 3 livelli di rientro prima ancora di parlare del fatto che i corpi delle istruzioni del flusso di controllo siano frastagliata.
PeterL

3
@LightnessRacesinOrbit Interpreto il commento "Le schede sono 8 caratteri" per non significare che è necessario visualizzare personalmente le schede come larghe 8 nel proprio editor, ma ai fini di altre regole nella guida di stile (come "Il limite sulla lunghezza delle linee è 80 colonne e questo è un limite fortemente preferito. ") si devono considerare le tab come 8 colonne, questo è rilevante anche per altre regole riguardanti l'allineamento degli argomenti nelle chiamate di funzione. Ancora una volta, non penso che l'intento di quella riga ti stia forzando a visualizzare le schede in quel modo, ho già fatto patch del kernel con 4 ampie schede e ridisposto il codice alla fine.
Valità il

4
@underscore_d: Sembra che mi sbagli: Outside of comments, documentation and except in Kconfig, spaces are never used for indentation, and the above example is deliberately broken.- 6 paragrafi in basso dalla citazione nel PO.
slebetman,

16

Il punto è lo stesso di qualsiasi costrutto di controllo del flusso: se il codice è difficile da capire, è necessario riformattare. Se stai facendo una semplice manipolazione di un array multidimensionale, può essere appropriato disporre di loop nidificati in profondità cinque o sei, a condizione che la logica nel ciclo più interno sia semplice. Tuttavia, se stai elaborando una complicata logica di business e il corpo del tuo loop è una dozzina di righe o più, probabilmente non vorrai annidare più di un loop in profondità. Puoi provare a calcolare la complessità ciclomatica del codice, ma ciò che si riduce davvero è la leggibilità e la manutenibilità del codice in questione.


11
Esattamente. È troppo facile suggerire che Torvalds è un lunatico. (Lo è, ovviamente.) Potrebbe essere troppo rigido per i tuoi gusti, ma sta descrivendo una vera preoccupazione per lo sviluppo che causa problemi reali. Non devi fare esattamente quello che dice, ma dovresti pensare al perché lo sta dicendo.
Scant Roger,

7
@ScantRoger In realtà, la citazione di Torvalds sembra troppo rigida solo se non si ottiene il suo senso dell'umorismo. Come ricordo, prima nello stesso documento, suggerisce di stampare una copia delle linee guida di stile di codifica GNU, solo per bruciarle in una sorta di cerimonia. Difficilmente lo prenderai sul serio, vero? In questa citazione, il suo punto principale è definire il rientro per il kernel di Linux in otto spazi, niente di più e niente di meno, questo è ciò su cui è rigido. L'ultima frase serve solo a sottolineare quel punto, per non dire che non si devono usare più livelli di rientro - nessuna rigidità implicita.
cmaster

1
@cmaster Grazie per il contesto, giusto! In risposta alla tua domanda, non prendo quasi nulla sul serio. ;)
Scant Roger,

2
@cmaster e quindi si leggono le sue risposte alle richieste pull di github e alla lunghezza della riga dei messaggi di commit. È un caso totale.
Gusdor,

3
Bruciare cerimoniosamente le linee guida sulla codifica GNU potrebbe non essere effettivamente necessario, ma è del tutto in ordine in qualsiasi momento.
dmckee,

13

Linus stava scherzando?

Il pezzo è scritto in uno stile giocoso che suggerisce che l'autore abbia familiarità con il modo in cui lo stile di programmazione è discusso tra i professionisti seri: tutti abbiamo le nostre preferenze e le difendiamo rabbiosamente, ma con la lingua almeno parzialmente nella guancia. Comprendiamo perfettamente che gran parte di esso è solo una questione di gusti personali. Dice, in così tante parole, "Coding style is very personal, and I won't _force_ my views on anybody"almeno al di fuori del codice che mantiene personalmente. Ma la coerenza di stile in un determinato progetto è un'ottima idea. Preferirei piuttosto codificare uno stile che non mi piace piuttosto che occuparmi di più stili in una data funzione.

Ecco un esempio di scrittura chiaramente giocosa:

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

Playful (1).

È probabilmente un buon consiglio cercare di evitare che il rientro sfugga al controllo, anche se un massimo di tre livelli potrebbe essere iperbolico. Non intendo fare il grep sul sorgente del kernel e contare le sequenze di quattro caratteri tab, ma scommetto che potresti trovarne almeno uno scritto da Torvalds.

D'altra parte, se qualcuno può scrivere il kernel Linux senza spesso superare i tre livelli di rientro, un limite di tre livelli potrebbe essere un esercizio che vale la pena provare per un po 'nel tuo codice, solo per vedere dove ti porta. Questo non è come un cambiamento di sesso, lo sai. Non è un impegno a vita.

Se incontri qualcuno su Internet che pensa di capire molto meglio la programmazione di Torvalds (2), allora sai che tipo di persone piace parlare in grande su Internet.

D'altra parte, ha torto criminalmente sulle schede di otto spazi. Questo è il delirio di un uomo che dovrebbe essere tenuto in punizione e alimentato attraverso una fessura. Quattro spazi sono ovviamente corretti.

(1) Ma nota come posiziona erroneamente uno spazio prima delle ellissi e due spazi dopo di loro e due spazi dopo un punto. SBAGLIATO, SBAGLIATO, SBAGLIATO. E poi ha il coraggio sfacciato per castigare gli eretici. L'eretico sei tu, Torvalds! SEI TU!

(2) Se si desidera parlare di " capire come progettare un sistema di controllo del codice sorgente ", potrebbe esserci spazio per il dibattito.

Nota: Gentile collega che ha inviato più volte la stessa modifica: la formattazione del materiale citato viene mantenuta esattamente come l'autore voleva che fosse. Questo perché proviene da un saggio sulla formattazione del testo a larghezza fissa, scritto in testo a larghezza fissa, da qualcuno che ha dato una buona dose di pensiero alla formattazione del testo a larghezza fissa. La formattazione è una parte consapevole e intenzionale dell'intento dell'autore ed è rilevante per l'argomento.

Inoltre, ho fatto riferimento a tale formattazione nel mio testo. Se togli la pre-formattazione, la mia nota a piè di pagina (1) diventa incomprensibile. Se la preformattazione viene rimossa, lo stesso dovrebbe essere il testo nella mia nota a piè di pagina (1) che si riferisce alle coppie di spazi dopo i punti di arresto completi alla fine delle frasi. Riesco comunque a vedere una logica per rimuovere quella nota in calce, dato che è meno divertente di quanto sembrasse quando l'ho scritta. Ma rimuovere la formattazione senza rimuovere la nota a piè di pagina non è utile.


3
Risposta meravigliosa. Uno dei casi che meriterebbe un +2 ... (Nota: non ci sono spazi sbagliati .in questo commento ;-))
cmaster

2
Il paragrafo introduttivo di Linus che hai sottolineato è molto importante, quindi grazie per averlo fatto! Penso che la prima frase sia anche molto importante per il contesto, in particolare preferred coding stylecosì comebut this is what goes for anything that I have to be able to maintain
Chris Haas il

9

Linus ha uno stile di linguaggio molto schietto e un secco senso dell'umorismo, ma in questo caso non stava scherzando. Ci sono situazioni in cui un algoritmo necessita di nidificazione più profonda di due livelli, ma puoi farlo usando altri mezzi oltre a indentare il tuo codice. La guida di stile del kernel Linux preferisce fortemente questi altri metodi, a causa della difficoltà di mantenere cicli profondamente annidati, ed è ciò che Linus sta dicendo qui.

Per alcuni esempi di metodi alternativi, è possibile utilizzare la ricorsione, suddividere i loop interni nelle proprie funzioni o creare strutture di dati intermedie.

L'annidamento eccessivo è uno di quei casi che è più facile da scrivere, ma più difficile da leggere. Impostare una profondità di tabulazione grande è il modo di Linus di rendere anche più fastidioso scrivere.


3

Ci sono molte domande in cui il consiglio è diverso per qualcuno che fa la domanda che per qualcuno che non lo fa. Se chiedi "Dovrei mai avere dei loop nidificati a più di due livelli di profondità", allora per te, la persona che fa quella domanda, la risposta è NO. Se lo chiedi, allora non farlo. Se hai abbastanza esperienza che non devi chiedere, allora sai qual è la risposta corretta in ogni caso. E non discutere se non sei d'accordo con la risposta, perché la risposta non fa per te.


1

Questo sembrerebbe un caso da manuale della coda che scuote il cane.

Se hai un display a 80 caratteri, ovviamente proverai ad adattare il codice nel miglior modo possibile anche se non produce la struttura migliore per il codice .

Affrontare il resto dei tuoi punti a testa alta:

Ho pensato che fosse una pratica inaccettabile.

Penso che stai leggendo troppo in questo. Resistete alla tentazione di prendere tutto ciò che leggete come vangelo senza comprendere adeguatamente il contesto.

Stava scherzando?

Difficile accertare il contesto, ma vedi il mio punto originale sopra.

Dipende dalla lingua o dall'applicazione?

Così tanto. Prendi qualsiasi linguaggio mainframe / midrange in cui è probabile che tu stia codificando su un terminale (o emulatore di terminale).

Ci sono alcune cose che hanno assolutamente bisogno di tre o più livelli di looping?

Sì, è molto comune in alcuni algoritmi di forza bruta. Vedi Problema 31 su Project Euler. Questo è un classico esempio di un problema che potrebbe essere risolto con la forza bruta usando un numero di loop (8 per l'esattezza).


1
Sembra che il Problema 31 non richieda la forza bruta e possa essere risolto usando un algoritmo di programmazione dinamica (modifica: il che significa che la struttura del codice non è la migliore se si utilizza una procedura bruteforce). Inoltre, il punto di Linus è che se il tuo codice richiede molti livelli di rientro, probabilmente non è la struttura migliore per il codice.
Vincent Savard,

2
@VincentSavard Non ha mai detto che richiedeva forza bruta. Non sono d'accordo con il tuo secondo punto - a volte è l'approccio più chiaro e più conciso, per non parlare del più efficace in alcuni casi.
Robbie Dee,

1
Con quel tipo di problema di solito non indento i loop. Penso di avere un caso con 20 loop nidificati, assolutamente banale da scrivere, e nessun rientro in modo da poter vedere che i loop erano quasi identici.
gnasher729,

1
@RobbieDee: Il mio punto è che il tuo esempio di un problema risolto da molti loop è che il tuo algoritmo non è efficiente come una soluzione di programmazione dinamica, che non richiede tanti livelli di rientro. Quindi, come ha detto Linus, i tuoi livelli di rientro possono essere rimossi usando una soluzione migliore. Hai anche frainteso il mio secondo punto perché sono d'accordo con quello che hai detto. A volte , è la soluzione migliore. A volte non è spesso e non è probabile.
Vincent Savard,

1
La citazione di Linus dice più o meno esplicitamente che se un codice richiede qualcosa come il bruteforcing di quel Problema-31, allora sei fregato comunque - non sarà veloce né semplice, e le operazioni del kernel devono essere veloci e semplici. Includere qualsiasi algoritmo O (n ^ 4) nel kernel è un rischio significativo di problemi di prestazioni o denial of service, quindi in questo contesto la raccomandazione avverte semplicemente che questo è un segno di codice che può essere fondamentalmente inappropriato e voluto in Linux.
Peteris,

0

Linus stava scherzando?

No, queste sono le linee guida ufficiali.

Dipende dalla lingua o dall'applicazione?

Le linee guida per la codifica dipendono generalmente dalla lingua e dall'applicazione, tuttavia il codice profondamente annidato tassa sempre il lettore.

Il problema con il codice nidificato è che in generale aumenta la complessità ciclomatica: vale a dire, più il codice è nidificato, più potenziali percorsi di esecuzione esistono all'interno della funzione. Un'esplosione combinatoria di potenziali percorsi di esecuzione rende difficile ragionare sul codice, e quindi dovrebbe essere evitato in generale.

Allora perché 3? Una linea guida soggettiva di codifica è sia difficile da applicare che impossibile da applicare automaticamente. Impostare una linea guida di codifica oggettiva sul massimo livello di rientro richiede di concordare un numero: nel kernel Linux hanno scelto 3.

È arbitrario e apparentemente sufficiente per loro.

Ci sono alcune cose che hanno assolutamente bisogno di tre o più livelli di looping?

Per quanto riguarda l'algoritmo, probabilmente, tuttavia in linguaggi sufficientemente espressivi è sempre possibile riformattare il codice in blocchi più piccoli (con funzioni o chiusure).

Ovviamente puoi scrivere codice offuscato con piccoli annidamenti e molte piccole funzioni che si chiamano senza mai scrivere il loro contratto ...

... tuttavia, le piccole funzioni con contratti chiari sono molto più facili da controllare rispetto alle grandi funzioni con contratti chiari in generale.


2
Mentre questa potrebbe essere la linea guida ufficiale, è banale trovare luoghi nel codice del kernel in cui la linea guida non viene applicata.
MikeB,

1
@MikeB: tutti i motivi per applicare automaticamente le linee guida ...
Matthieu M.

1
@MatthieuM. Sei sicuro di comprendere la differenza tra linee guida e requisiti obbligatori? Come regola generale (una linea guida se vuoi), le linee guida sono più simili a raccomandazioni e non sono applicate.
Brendan,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.