All'interno di un ciclo continuo, devo spostare la condizione di interruzione nel campo condizione, se possibile? [chiuso]


48

A volte ho bisogno di loop che hanno bisogno di una pausa come questa:

for(int i=0;i<array.length;i++){
    //some other code
    if(condition){
        break;
    }
}

Mi sento a disagio con la scrittura

if(condition){
    break;
}

perché consuma 3 righe di codice. E ho scoperto che il loop può essere riscritto come:

                                   ↓
for(int i=0;i<array.length && !condition;i++){
    //some other code
}

Quindi la mia domanda è: è buona pratica spostare la condizione nel campo condizione per ridurre le linee di codici, se possibile?


7
Dipende da ciò che è conditionin particolare.
Bergi,

4
C'è una ragione per cui misra proibisce l'uso di "break" e "continue" all'interno dei loop. Vedi anche questo: stackoverflow.com/q/3922599/476681
BЈовић

35
Penso che il numero di righe in sé non sia il vero problema. Potrebbe essere scritto su una riga. se (condizione) si rompe; Il problema secondo me è la leggibilità. Personalmente non mi piace interrompere un loop diverso dall'uso della condizione loop.
Joe,

97
perché consuma 3 righe di codice Un motivo molto, molto, molto brutto per non amare nessuno stile. "Consumare [ing] righe di codice" dell'IMO è un punto tra irrilevante e positivo. Cercare di inserire troppa logica in troppe righe provoca codice illeggibile e bug difficili da trovare.
Andrew Henle,

3
Opinione forse impopolare: for(int i=0;i<array.length && !condition;i++)è relativamente rara e può essere trascurata da qualcuno che sta semplicemente scremando il codice; questo potrebbe essere un buon caso d'uso per un whileo do whileloop, che più spesso presenta più condizioni di interruzione nella definizione del loop.
Jules,

Risposte:


154

Quei due esempi che hai fornito non sono funzionalmente equivalenti. Nell'originale, il test delle condizioni viene eseguito dopo la sezione "qualche altro codice", mentre nella versione modificata viene eseguito per primo, all'inizio del corpo del ciclo.

Il codice non deve mai essere riscritto al solo scopo di ridurre il numero di righe. Ovviamente è un bel bonus quando funziona in questo modo, ma non dovrebbe mai essere fatto a spese della leggibilità o correttezza.


5
Capisco il tuo sentimento, ma ho mantenuto abbastanza codice legacy in cui il ciclo è lungo diverse centinaia di righe e ci sono più interruzioni condizionali. Rende molto difficile il debug quando ti aspetti che il codice arrivi all'interruzione condizionale che ti aspetti, ma il codice precedente esercitato è prima l'interruzione condizionale. Come gestisci quel codice? In brevi esempi come quello sopra, è facile dimenticare quel tipo di scenario fin troppo comune.
Berin Loritsch,

84
@BerinLoritsch: il modo corretto di gestirlo è quello di non avere loop lunghi diverse centinaia di righe!
Chris,

27
@Walfrat: se la riscrittura non è un'opzione, non c'è più davvero una domanda. Detto questo, di solito è possibile utilizzare in modo abbastanza sicuro un refattore di tipo "Metodo di estrazione" per spostare i blocchi di codice attorno ai quali dovrebbe accorciare il ciclo del metodo principale e, si spera, rendere più ovvio il flusso logico. È davvero un caso per caso. Rispetterò la mia regola generale di mantenere i metodi brevi e sicuramente mantenere la logica del ciclo successivo breve, ma non voglio provare a dire altro in senso generale, però ...
Chris,

4
La brevità di @AndrewHenle è leggibilità, almeno nel senso che l'aumento della verbosità riduce sempre la leggibilità e la manutenibilità. La riduzione della LOC si ottiene aumentando la densità e aumentando il livello di astrazione, ed è strettamente correlata con la manutenibilità come attestato in altre coppie Q / A proprio in questo sito.
Leushenko,

5
@Joe un costrutto Do-While è così raro che è incline a far inciampare gli sviluppatori che devono mantenere quel codice in un secondo momento. Alcuni sviluppatori non li hanno mai visti! Non sono sicuro che un do-while migliorerebbe affatto la leggibilità - semmai aumenterebbe la difficoltà a comprendere il codice. Ad esempio, la mia base di codice attuale ha circa 15 anni e ha circa 2 milioni di LOC. Circa cinquanta sviluppatori l'hanno già toccato. Ha esattamente zero loop do-while!
T. Sar - Ripristina Monica il

51

Non compro l'argomento secondo cui "consuma 3 righe di codice" e quindi è un male. Dopotutto, potresti semplicemente scriverlo come:

if (condition) break;

e consuma solo una riga.

Se ifappare a metà del ciclo, ovviamente deve esistere come test separato. Tuttavia, se questo test appare alla fine del blocco, è stato creato un ciclo che ha un test continuo su entrambe le estremità del ciclo, aggiungendo eventualmente alla complessità del codice. Tenere presente, tuttavia, che a volte il if (condition)test può avere senso solo dopo l'esecuzione del blocco.

Ma supponendo che non sia così, adottando l'altro approccio:

for(int i=0;i<array.length && !condition;i++){
    //some other code
}

le condizioni di uscita sono mantenute insieme, il che può semplificare le cose (specialmente se " qualche altro codice " è lungo).

Naturalmente non c'è una risposta giusta qui. Quindi sii consapevole dei modi di dire della lingua, quali sono le convenzioni di codifica della tua squadra, ecc. Ma a conti fatti, adotterò il singolo approccio di test quando ha senso funzionale farlo.


14
@ jpmc26, Meglio? No. Stile alternativo valido? Ovviamente.
David Arno,

6
Meglio che nel più difficile sbagliare quando il codice viene modificato in un secondo momento e non presenta alcun notevole svantaggio.
jpmc26,

4
Tenerli uniti a causa di "contenuti del ciclo potrebbe essere lungo" sembra essere un argomento debole. Quando il tuo contenuto è difficile da leggere, allora hai altri problemi oltre a salvare le due righe di codice.
BlueWizard,

12
@ jpmc26 Non sono d'accordo. Le parentesi graffe sono disordine superfluo nei più brevi condizionali a una fodera secondo questa risposta. Senza è più elegante e più facile per gli occhi. Sono come un operatore condizionale ternario. Non ho mai dimenticato di aggiungere le parentesi graffe mentre le suddividevo in blocchi di istruzioni più grandi; Dopotutto sto già aggiungendo nuove righe.
benxyzzy,

3
È tutta una preferenza di stile, ma se uno afferma che è più sicuro, non sarei d'accordo con quel ragionamento. Io stesso preferisco senza parentesi graffe e nella riga successiva, ma ciò non significa che sia più o meno valido di altri stili
phflack

49

Questo tipo di domanda ha suscitato il dibattito quasi quanto la programmazione. Per lanciare il mio cappello sul ring, sceglierei la versione con la breakcondizione ed ecco perché (per questo caso specifico ):

Il forciclo è proprio lì per specificare i valori iteranti nel ciclo. Codificare la condizione di rottura all'interno che confonde la logica IMO.

La presenza di un elemento separato breakchiarisce chiaramente che durante l'elaborazione normale, i valori sono quelli specificati nel forciclo e l'elaborazione deve continuare tranne nel caso in cui si verifichi la condizione.

Come un po 'di background, ho iniziato a scrivere codice a break, quindi ho messo le condizioni in forloop per anni (sotto la direzione di un programmatore eccezionale) e poi sono tornato allo breakstile perché mi sentivo molto più pulito (ed era lo stile prevalente in i vari tomi di codice che stavo consumando).

È un'altra di quelle guerre sante alla fine della giornata - alcuni dicono che non dovresti mai uscire artificialmente da un ciclo, altrimenti non è un vero ciclo. Fai ciò che ritieni sia leggibile (sia per te che per gli altri). Se ridurre le sequenze di tasti è davvero la tua passione, vai a golf nel fine settimana - birra opzionale.


3
Se la condizione ha un buon nome, come "found" (ad esempio, stai cercando qualcosa nell'array), allora è una buona idea metterlo in testa al ciclo for.
user949300,

1
Mi è stato detto che i forloop vengono utilizzati quando si conosce il numero di iterazioni (ad esempio quando non vi è alcuna condizione di interruzione ma la fine dell'array / elenco) e whilequando non lo sono. Questo sembra essere il caso di un whileciclo. Se la leggibilità è un problema idx+=1all'interno di un ciclo è molto più pulito da leggere rispetto a un if/elseblocco
Laiv,

Non puoi semplicemente inserire la condizione di interruzione nel ciclo se c'è del codice che la segue, quindi almeno dovresti scrivere "if (condizione) {found = true; continue;}
gnasher729

Siamo così abituati ai for(int i=0; i<something;i++)loop che immagino che metà degli sviluppatori non ricordi davvero che i forloop possono essere usati in modo diverso, e quindi è difficile capire la &&conditionparte.
Ralf Kleberhoff

@RalfKleberhoff Effettivamente. Ho visto molti nuovi sviluppatori esprimere la sorpresa che le espressioni delle dichiarazioni tripart siano tutte facoltative. Non ricordo nemmeno l'ultima volta che ho visto for(;;)...
Robbie Dee

43

fori loop sono per l'iterazione su qualcosa di 1 - non sono solo lol-Let's-pull-some-random-stuff-from-the-body-and-put-them-in-the-loop-header - le tre espressioni hanno molto significati specifici:

  • Il primo è per inizializzare l' iteratore . Può essere un indice, un puntatore, un oggetto di iterazione o altro, purché sia ​​utilizzato per l' iterazione .
  • Il secondo è per verificare se siamo arrivati ​​alla fine.
  • Il terzo è per far avanzare l'iteratore.

L'idea è quella di separare la gestione dell'iterazione ( come iterare) dalla logica all'interno del ciclo ( cosa fare in ogni iterazione). Molte lingue di solito hanno un ciclo per ogni che ti allevia dai dettagli dell'iterazione, ma anche se la tua lingua non ha quel costrutto o se non può essere usata nel tuo scenario, dovresti comunque limitare l'intestazione del ciclo alla gestione dell'iterazione.

Quindi, devi chiederti: è la tua condizione sulla gestione dell'iterazione o sulla logica all'interno del ciclo? È probabile che si tratti della logica all'interno del loop, quindi dovrebbe essere verificata all'interno del loop piuttosto che nell'intestazione.

1 A differenza di altri loop, che "aspettano" che accada qualcosa. A for/ foreachloop dovrebbe avere il concetto di un "elenco" di elementi su cui scorrere - anche se tale elenco è pigro, infinito o astratto.


5
Questo è stato il mio primo pensiero quando ho letto la domanda. Deluso, ho dovuto scorrere una mezza dozzina di risposte per vedere qualcuno dirlo
Nemo,

4
Umm ... tutti i loop sono per iterazione - ecco cosa significa la parola "iterazione" :-). Suppongo che intendi qualcosa come "iterare su una raccolta" o "iterare usando un iteratore"?
psmears,

3
@psmears OK, forse ho scelto la parola sbagliata - l'inglese non è la mia lingua madre e quando penso all'iterazione penso a "iterazione su qualcosa". Riparerò la risposta.
Idan Arye,

Un caso intermedio è dove la condizione è simile someFunction(array[i]). Ora entrambe le parti della condizione riguardano la cosa su cui stai ripetendo, e avrebbe senso combinarle con &&l'intestazione del ciclo.
Barmar,

Rispetto la tua posizione, ma non sono d'accordo. Penso che i forloop a cuore siano contatori, non loop su un elenco, e questo è supportato dalla sintassi di fornelle lingue precedenti (queste lingue spesso avevano una for i = 1 to 10 step 2sintassi e il stepcomando - anche consentendo un valore iniziale che non è l'indice del primo element - indica un contesto numerico, non un contesto di elenco). L' unico caso d'uso che penso supporta i forloop in quanto solo iterando su un elenco è il parallelismo, e ciò richiede comunque una sintassi esplicita ora per non interrompere il codice facendo, ad esempio, un ordinamento.
Logan Pickup

8

Consiglio di utilizzare la versione con if (condition). È più leggibile e più facile da eseguire il debug. Scrivere 3 righe di codice in più non ti romperà le dita, ma renderà più facile per la prossima persona capire il tuo codice.


1
Solo se (come è stato commentato) il blocco loop non ha centinaia di righe di codice e la logica all'interno non è scienza missilistica. Penso che il tuo argomento sia ok, ma mi manca qualcosa di simile naming thingsper capire cosa sta succedendo e quando viene soddisfatta la condizione di rottura.
Laiv

2
@Laiv Se il blocco del ciclo contiene centinaia di righe di codice, allora ci sono problemi più grandi della domanda su dove scrivere la condizione di interruzione.
Aleksander,

Ecco perché ho suggerito di contestualizzare la risposta, perché non è sempre vero.
Laiv

8

Se stai ripetendo una raccolta in cui alcuni elementi devono essere ignorati, ti consiglio una nuova opzione:

  • Filtra la tua raccolta prima di iterare

Sia Java che C # lo rendono relativamente banale. Non sarei sorpreso se C ++ avesse un modo per creare un iteratore di fantasia per saltare alcuni elementi che non si applicano. Ma questa è davvero un'opzione solo se la tua lingua preferita la supporta e il motivo che stai usando breakè perché ci sono condizioni sul fatto che tu elabori un elemento o meno.

Altrimenti c'è un ottimo motivo per rendere la tua condizione parte del ciclo for - supponendo che la sua valutazione iniziale sia corretta.

  • È più facile vedere quando un ciclo termina presto

Ho lavorato sul codice in cui il tuo ciclo for prende alcune centinaia di righe con diversi condizionali e qualche matematica complessa in corso lì. I problemi che incontri con questo sono:

  • break i comandi nascosti nella logica sono difficili da trovare e talvolta sorprendenti
  • Il debug richiede di passare attraverso ogni iterazione del ciclo per capire veramente cosa sta succedendo
  • Gran parte del codice non dispone di refactoring facilmente reversibili o unit test per aiutare con l'equivalenza funzionale dopo una riscrittura

In tale situazione, raccomando le seguenti linee guida in ordine di preferenza:

  • Filtra la raccolta per eliminare la necessità di un breakse possibile
  • Rendi la parte condizionale delle condizioni del ciclo for
  • Posizionare i condizionali con il breaknella parte superiore del loop, se possibile
  • Aggiungi un commento su più righe per attirare l'attenzione sull'interruzione e perché è lì

Queste linee guida sono sempre soggette alla correttezza del codice. Scegli l'opzione che può rappresentare al meglio l'intento e migliorare la chiarezza del codice.


1
Gran parte di questa risposta va bene. Ma ... posso vedere come il filtraggio di una raccolta potrebbe eliminare la necessità di continuedichiarazioni, ma non vedo come possano essere di grande aiuto break.
user949300,

1
@ user949300 Un takeWhilefiltro può sostituire le breakistruzioni. Se ne hai uno - Java ad esempio li ottiene solo su Jave 9 (non che sia così difficile implementarlo da solo)
Idan Arye,

1
Ma in Java è ancora possibile filtrare prima dell'iterazione. Utilizzo dei flussi (1.8 o successivi) o utilizzo delle raccolte Apache (1.7 o precedenti).
Laiv

@IdanArye - ci sono librerie aggiuntive che aggiungono tali servizi all'API dei flussi Java (ad esempio JOO-lambda )
Jules,

@IdanArye non ha mai sentito parlare di un takeWhile prima del tuo commento. Il collegamento esplicito del filtro a un ordinamento mi sembra insolito e confuso, poiché in un filtro "normale" ogni elemento può restituire vero o falso, indipendentemente da ciò che viene prima o dopo. In questo modo puoi eseguire le cose in parallelo.
user949300

8

Consiglio vivamente di adottare l'approccio di minima sorpresa a meno che tu non ottenga un beneficio significativo facendo diversamente.

Le persone non leggono tutte le lettere quando leggono una parola e non leggono tutte le parole quando leggono un libro - se sono abili nella lettura, guardano i contorni delle parole e delle frasi e lasciano che il cervello si riempia del resto .

Quindi è probabile che lo sviluppatore occasionale supponga che questo sia solo uno standard per il loop e nemmeno lo guardi:

for(int i=0;i<array.length&&!condition;i++)

Se vuoi usare quello stile a prescindere, ti consiglio di cambiare le parti for(int i=0;i<e ;i++)dire al cervello del lettore che questo è uno standard per il loop.


Un altro motivo per andare con if- breakè che non si può sempre utilizzare il tuo approccio con la for-condition. Devi usare if- breakquando la condizione di interruzione è troppo complessa per nascondersi all'interno di forun'istruzione o si basa su variabili accessibili solo all'interno del forciclo.

for(int i=0;i<array.length&&!((someWidth.X==17 && y < someWidth.x/2) || (y == someWidth.x/2 && someWidth.x == 18);i++)

Quindi, se decidi di andare con if- break, sei coperto. Ma se si decide di andare con forCircostanze, è necessario utilizzare un mix di if- breake forCircostanze. Per essere coerenti, dovrai spostare le condizioni avanti e indietro tra le condizioni fore il if- breakogni volta che le condizioni cambiano.


4

La tua trasformazione presuppone che qualsiasi cosa conditionvenga valutata truementre entri nel ciclo. Non possiamo commentare la correttezza di ciò in questo caso perché non è definito.

Avendo successo nel "ridurre le righe di codice" qui, puoi continuare a guardare

for(int i=0;i<array.length;i++){
    // some other code
    if(condition){
        break;
    }
    // further code
}

e applicare la stessa trasformazione, arrivando a

for(int i=0;i<array.length && !condition;i++){
    // some other code
    // further code
}

Che è una trasformazione non valida, come ora fai incondizionatamente further codedove prima non l'hai fatto.

Uno schema di trasformazione molto più sicuro è quello di estrarre some other codee valutare conditionuna funzione separata

bool evaluate_condition(int i) // This is an horrible name, but I have no context to improve it
{
     // some other code
     return condition;
}

for(int i=0;i<array.length && !evaluate_condition(i);i++){
    // further code
}

4

TL; DR Fai ciò che legge meglio nella tua particolare circostanza

Facciamo questo esempio:

for ( int i = 1; i < array.length; i++ ) 
{
    if(something) break;
    // perform operations
}

In questo caso non vogliamo che nessuno dei codici nel ciclo for somethingvenga eseguito se è vero, quindi somethingè ragionevole spostare il test nel campo condizione.

for ( int i = 1; i < array.length && !something; i++ )

Poi di nuovo, a seconda se somethingpuò essere impostato trueprima del loop, ma non al suo interno, questo potrebbe offrire maggiore chiarezza:

if(!something)
{
    for ( int i = 1; i < array.length; i++ )
    {...}
}

Ora immagina questo:

for ( int i = 1; i < array.length; i++ ) 
{
    // perform operations
    if(something) break;
    // perform more operations
}

Stiamo inviando un messaggio molto chiaro lì. Se somethingdiventa vero durante l'elaborazione dell'array, abbandonare l'intera operazione a questo punto. Per spostare il segno di spunta nel campo condizione è necessario:

for ( int i = 1; i < array.length && !something; i++ ) 
{
    // perform operations
    if(!something)
    {
        // perform more operations
    }
}

Probabilmente, il "messaggio" è diventato confuso e stiamo verificando la condizione di errore in due punti: cosa succede se la condizione di errore cambia e dimentichiamo uno di quei luoghi?

Naturalmente ci sono molte altre forme diverse per un ciclo e una condizione, tutte con le loro sfumature.

Scrivi il codice in modo che legga bene (questo è ovviamente in qualche modo soggettivo) e conciso. Al di fuori di una serie draconiana di linee guida per la codifica, tutte le "regole" hanno eccezioni. Scrivi ciò che ritieni esprime meglio il processo decisionale e minimizza le possibilità di errori futuri.


2

Mentre può essere attraente perché vedi tutte le condizioni nell'intestazione del loop e breakpuò essere confuso all'interno di blocchi di grandi dimensioni, un forloop è un modello in cui la maggior parte dei programmatori si aspetta che eseguano il loop su un certo intervallo.

Soprattutto da quando lo fai, l'aggiunta di una condizione di interruzione aggiuntiva può causare confusione ed è più difficile vedere se è funzionalmente equivalente.

Spesso una buona alternativa è usare un ciclo whileo do {} whileche controlla entrambe le condizioni, supponendo che l'array abbia almeno un elemento.

int i=0
do {
    // some other code
    i++; 
} while(i < array.length && condition);

Usando un ciclo che controlla solo una condizione e non esegue il codice dall'intestazione del ciclo, rendi molto chiaro quando la condizione è selezionata e qual è la condizione effettiva e qual è il codice con effetti collaterali.


Nonostante questa domanda abbia già delle buone risposte, volevo esprimere espressamente il mio voto perché fa un punto importante: per me, avere qualcosa di diverso da un semplice controllo associato nel for-loop ( for(...; i <= n; ...)) rende il codice più difficile da leggere perché ho fermarsi e pensare a ciò che sta accadendo qui e potrebbe perdere del tutto la condizione al primo passaggio perché non mi aspetto che sia lì. Per me, un forciclo implica che stai eseguendo il ciclo su un certo intervallo, se esiste una condizione di uscita speciale che dovrebbe essere resa esplicita con un if, oppure puoi usare un while.
CompuChip,

1

Questo è negativo, poiché il codice deve essere letto dagli umani - i forloop non idiomatici sono spesso confusi da leggere, il che significa che è più probabile che i bug si nascondano qui, ma il codice originale potrebbe essere stato possibile migliorare mantenendo entrambi breve e leggibile.

Se quello che vuoi è trovare un valore in un array (l'esempio di codice fornito è in qualche modo generico, ma potrebbe anche essere questo modello), puoi semplicemente provare esplicitamente a usare una funzione fornita da un linguaggio di programmazione appositamente a tale scopo. Ad esempio (pseudo-codice, poiché non è stato specificato un linguaggio di programmazione, le soluzioni variano a seconda di un determinato linguaggio).

array.find(item -> item.valid)

Ciò dovrebbe abbreviare sensibilmente la soluzione, semplificandola nel momento in cui il codice dice specificamente ciò di cui hai bisogno.


1

Poche ragioni secondo me dicono che non dovresti.

  1. Ciò riduce la leggibilità.
  2. L'output potrebbe non essere lo stesso. Tuttavia, può essere eseguito con un do...whileciclo (non while) poiché la condizione viene verificata dopo l'esecuzione di un codice.

Ma soprattutto, considera questo,

for(int i=0;i<array.length;i++){

    // Some other code
    if(condition1){
        break;
    }

    // Some more code
    if(condition1){
        break;
    }

    // Some even more code
}

Ora, puoi davvero raggiungerlo aggiungendo queste condizioni in quella forcondizione?


Che cos'è " apertura "? Intendi " opinione "?
Peter Mortensen,

Cosa intendi con "Poche ragioni in apertura che dicono, non dovresti" ? Puoi elaborare?
Peter Mortensen,

0

Penso che rimuovere il breakpuò essere una buona idea, ma non per salvare righe di codice.

Il vantaggio di scriverlo senza breakè che la post-condizione del loop è ovvia ed esplicita. Quando finisci il ciclo ed esegui la riga successiva del programma, la condizione del tuo ciclo i < array.length && !conditiondeve essere stata falsa. Pertanto, o iera maggiore o uguale alla lunghezza dell'array o conditionè vero.

Nella versione con break, c'è un gotcha nascosto. La condizione del ciclo dice che il ciclo termina quando hai eseguito qualche altro codice per ciascun indice di array valido, ma esiste in realtà breakun'istruzione che potrebbe terminare il ciclo prima di quello, e non lo vedrai se non riesci a rivedere il codice del ciclo molto attentamente. E gli analizzatori statici automatizzati, incluso l'ottimizzazione dei compilatori, potrebbero avere difficoltà a dedurre anche quali sono le post-condizioni del loop.

Questa era la ragione originale per cui Edgar Dijkstra scrisse "Goto Considered Harmful". Non era una questione di stile: uscire da un ciclo rende difficile formalmente ragionare sullo stato del programma. Ho scritto molte break;dichiarazioni nella mia vita e, una volta da adulto, ho anche scritto un goto(per uscire da più livelli di un ciclo interno critico per le prestazioni e poi continuare con un altro ramo dell'albero di ricerca). Tuttavia, sono molto più propenso a scrivere returnun'istruzione condizionale da un ciclo (che non esegue mai il codice dopo il ciclo e quindi non può confondere le sue post-condizioni) rispetto a a break.

poscritto

In effetti, il refactoring del codice per fornire lo stesso comportamento potrebbe effettivamente richiedere più righe di codice. Il tuo refactoring potrebbe essere valido in particolare per qualche altro codice o se possiamo provare che conditioninizierà falso. Ma il tuo esempio è equivalente nel caso generale a:

if ( array.length > 0 ) {
  // Some other code.
}
for ( int i = 1; i < array.length && !condition; i++ ) {
  // Some other code.
}

Se qualche altro codice non è un one-liner, potresti spostarlo in una funzione in modo da non ripetere te stesso, e quindi hai quasi rifattorizzato il tuo loop in una funzione ricorsiva della coda.

Riconosco che questo non era il punto principale della tua domanda, e lo stavi mantenendo semplice.


Il problema non è che l'interruzione rende difficile discutere del codice, il problema è che l'interruzione in punti casuali rende il codice complicato. Se ciò è effettivamente necessario, non è difficile discutere a causa dell'interruzione, ma perché il codice deve fare cose complicate.
gnasher729,

Ecco perché non sono d'accordo: se sai che non ci sono breakdichiarazioni, allora sai qual è la condizione del ciclo e che il ciclo si interrompe quando tale condizione è falsa. Se c'è un break? Quindi ci sono diversi modi in cui il loop potrebbe terminare e, nel caso generale, capire quando uno di loro potrebbe fermare il loop sta letteralmente risolvendo il problema dell'Halting. In pratica, la mia più grande preoccupazione è che il loop sembri fare una cosa, ma in realtà, chiunque abbia scritto il codice dopo che il loop ha trascurato un caso limite sepolto nella sua complessa logica. È difficile perdere la roba nella condizione del loop.
Davislor,

0

Sì, ma la tua sintassi è non convenzionale e quindi male (tm)

Spero che la tua lingua supporti qualcosa del genere

while(condition) //condition being a condition which if true you want to continue looping
{
    do thing; //your conditionally executed code goes here
    i++; //you can use a counter if you want to reference array items in order of index
}

2
forse avrei dovuto usare una parola diversa per "condizione" Non intendevo letteralmente indicare la stessa identica condizione nell'esempio
Ewan,

1
Non solo il ciclo for non è nemmeno lontanamente non convenzionale, non c'è assolutamente alcun motivo per cui un ciclo while di questo modulo sia migliore di un equivalente per loop. In effetti la maggior parte delle persone direbbe che è peggio, come gli autori delle Linee guida
Sean Burton,

2
Alcune persone odiano solo le alternative creative. O guardare un problema in un modo diverso rispetto a quello che si è messo in testa. O più in generale, asini intelligenti. Sento il tuo dolore fratello!
Martin Maat,

1
@sean mi dispiace amico, so che le persone odiano sentirsi dire che hanno torto, ma ti rimando a es.77
Ewan

2
Questo whileenfatizza il mostro nel codice - l'eccezione altrimenti nascosta nel codice di routine / banale. La logica di business è al centro e al centro, la meccanica del loop è inclusa. Evita la reazione "aspetta un minuto ..." forall'alternativa del loop. Questa risposta risolve il problema. voto assolutamente positivo.
radarbob

0

Se sei preoccupato che forun'affermazione troppo complessa sia difficile da leggere, questa è sicuramente una preoccupazione valida. Anche lo spreco di spazio verticale è un problema (sebbene meno critico).

(Personalmente non mi piace la regola secondo cui le ifistruzioni devono sempre avere parentesi graffe, che è in gran parte la causa dello spazio verticale sprecato qui. Nota che il problema sta sprecando spazio verticale. L' uso dello spazio verticale con linee significative non dovrebbe essere un problema.)

Un'opzione è dividerlo su più righe. Questo è sicuramente ciò che dovresti fare se stai usando foristruzioni davvero complesse , con più variabili e condizioni. (Questo è per me un migliore utilizzo dello spazio verticale, dando quante più righe possibile qualcosa di significativo.)

for (
   int i = 0, j = 0;
   i < array.length && !condition;
   i++, j++
) {
   // stuff
}

Un approccio migliore potrebbe essere quello di provare a utilizzare una forma più dichiarativa e meno imperativa. Questo varierà a seconda della lingua, oppure potresti dover scrivere le tue funzioni per abilitare questo genere di cose. Dipende anche da cosa sia conditioneffettivamente. Presumibilmente deve essere in grado di cambiare in qualche modo, a seconda di ciò che è nella matrice.

array
.takeWhile(condition)  // condition can be item => bool or (item, index) => bool
.forEach(doSomething); // doSomething can be item => void or (item, index) => void

Dividere forun'affermazione complessa o insolita in più righe è l'idea migliore in tutta questa discussione
user949300

0

Una cosa che è stata dimenticata: quando mi interrompo da un ciclo (non faccio tutte le possibili iterazioni, non importa quanto implementato), di solito è il caso che non solo voglio uscire dal ciclo, voglio anche sapere perché è successo . Ad esempio, se cerco un elemento X, non solo mi interrompo dal ciclo, voglio anche sapere che X è stato trovato e dove si trova.

In quella situazione, avere una condizione al di fuori del ciclo è abbastanza naturale. Quindi inizio con

bool found = false;
size_t foundIndex;

e nel ciclo scriverò

if (condition) {
    found = true;
    foundIndex = i;
    break;
}

con il ciclo for

for (i = 0; i < something && ! found; ++i)

Ora controllare la condizione nel loop è abbastanza naturale. La presenza o meno di un'istruzione "break" dipende dall'eventuale ulteriore elaborazione nel loop che si desidera evitare. Se è necessaria una certa elaborazione e altre no, potresti avere qualcosa di simile

if (! found) { ... }

da qualche parte nel tuo ciclo.

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.