'<' versus '! =' come condizione in un ciclo 'for'?


31

Supponi di avere il seguente forciclo *:

for (int i = 0; i < 10; ++i) {
    // ...
}

che potrebbe comunemente essere anche scritto come:

for (int i = 0; i != 10; ++i) {
    // ...
}

I risultati finali sono gli stessi, quindi ci sono dei veri argomenti per usare l'uno sull'altro? Personalmente uso il primo nel caso in cui iper qualche ragione vada in tilt e salti il ​​valore 10.

* Scusa l'uso di numeri magici, ma è solo un esempio.


18
La soluzione migliore per uno di questi è usare l'operatore freccia:int i = 10; while(i --> 0) { /* stuff */ }
corsiKa

@glowcoder l'operatore freccia è il mio preferito
Carson Myers,

3
@glowcoder, bello ma attraversa da dietro. Potresti non volerlo sempre.

2
@SuperElectric, analizza comewhile ((i--) > 0)
Kristopher Johnson il

17
Esiste una storia (probabilmente apocrifa) di un incidente industriale causato da un test del circuito del tempo per l'ingresso di un sensore! = MAX_TEMP. Sfortunatamente un giorno l'ingresso del sensore è passato da meno di MAX_TEMP a maggiore di MAX_TEMP senza passare attraverso MAX_TEMP. Il processo si surriscaldò senza essere rilevato e ne conseguì un incendio. A volte c'è una differenza tra! = E <.
Charles E. Grant,

Risposte:


59

Il motivo per scegliere l'uno o l'altro è a causa dell'intento e, di conseguenza, aumenta la leggibilità .

Intento: il ciclo dovrebbe durare fino a quando iè inferiore a 10, non fino a quando inon è uguale a 10. Anche se quest'ultimo può essere lo stesso in questo caso particolare, non è quello che vuoi dire, quindi non dovrebbe essere scritto così.

Leggibilità: il risultato della scrittura di ciò che intendi è che è anche più facile da capire. Ad esempio, se lo usi i != 10, qualcuno che legge il codice potrebbe chiedersi se all'interno del ciclo ci sia un modo per idiventare più grande di 10 e che il ciclo dovrebbe continuare la fordichiarazione, ma ciò non significa che le persone non lo facciano e di conseguenza i manutentori se lo aspettano).


3
No, il ciclo non dovrebbe funzionare finché iè "inferiore a 10", dovrebbe funzionare fino a quando non viene raggiunto il limite superiore (in questo caso). Quando si utilizza l'intervallo / iteratore gamma razionale ++ C, è molto più logico per esprimere questa via !=rispetto <.
Konrad Rudolph,

14
@Konrad Non sono affatto d'accordo. Il punto principale non è essere dogmatici, ma piuttosto scegliere il costrutto che meglio esprime l'intento della condizione. E quindi, se scegli di eseguire il loop di qualcosa che inizia da 0 e si sposta verso l'alto, allora <lo esprime in modo preciso.
Deckard,

6
@Konrad, ti manca il punto. L'operatore di incremento in questo ciclo rende ovvio che la condizione del ciclo è un limite superiore, non un confronto di identità. Se decrementassi, sarebbe un limite inferiore. Se stai ripetendo una raccolta non ordinata, l'identità potrebbe essere la condizione giusta.
Alex Feinman,

3
@Alex l'incremento non era il mio punto. Un intervallo non ha nemmeno necessariamente hanno un ordine. Si ripete ... beh, qualcosa fino a quando non raggiunge la fine. Ad esempio, elementi in un set di hash. In C ++, questo viene fatto usando iteratori e un forciclo. Usare <qui sarebbe sciocco. Inoltre, Deckard sembra riconoscerlo. Sono molto d'accordo con il suo commento in risposta al mio.
Konrad Rudolph,

1
Nota, se usi un buffer rotativo con puntatori di inseguimento, DEVI usare!=
SF.

48

Il <modello è generalmente utilizzabile anche se l'incremento sembra non essere esattamente 1.

Ciò consente un unico modo comune per eseguire i loop indipendentemente da come è stato effettivamente eseguito.


6
Permette anche idi modificare all'interno del loop in totale sicurezza, se necessario.
Matthew Scharley,

1
o se "i" viene modificato in modo totalmente pericoloso ... Un altro team ha avuto uno strano problema con il server. Ha continuato a riportare l'utilizzo della CPU al 100% e deve essere un problema con il server o il sistema di monitoraggio, giusto? No, ho trovato una condizione ciclica scritta da un "esperto programmatore senior" con lo stesso problema di cui stiamo parlando.
jqa,

12
Tranne il fatto che non tutti i C ++ per i loop possono essere utilizzati <, come gli iteratori su una sequenza, e quindi !=sarebbe un unico modo comune per eseguire i loop in C ++.
David Thornley,

@SnOrfus: non sto analizzando abbastanza quel commento. In generale non otterrai eccezioni affidabili per incrementare troppo un iteratore (anche se ci sono situazioni più specifiche in cui lo farai).
David Thornley,

Bene, seguo lo <schema perché richiede una sequenza di tasti anziché due con lo >schema e quattro con !=. È davvero una questione di efficienza simile al DOC.
Spoike,

21

In C ++ la raccomandazione di Scott Myers in C ++ più efficace (elemento 6) è di usare sempre la seconda a meno che non si abbia una ragione per non farlo perché significa che si ha la stessa sintassi per gli indici iteratore e intero in modo da poter scambiare senza problemi tra intiteratore e iteratore senza alcun cambiamento nella sintassi.

In altre lingue questo non si applica, quindi credo <sia probabilmente preferibile a causa del punto di Thorbjørn Ravn Andersen.

Tra l'altro, l'altro giorno stavo discutendo questo con un altro sviluppatore e ha detto che il motivo per preferire <sopra !=è perché ipotrebbe accidentalmente incrementa da più di una, e che potrebbe causare la condizione di interruzione di non essere rispettato; questo è l'IMO un carico di sciocchezze.


17
"carico di sciocchezze" ... fino al giorno in cui accidentalmente hai un i ++ in più nel corpo del loop.

2
+1, specialmente per "carico di sciocchezze", perché lo è. Se il corpo del loop aumenta accidentalmente il contatore, si hanno problemi molto più grandi.
Konrad Rudolph,

12
@B Tyler, siamo solo umani e prima si sono verificati errori più grandi. Considera <di essere una programmazione più difensiva di !=allora ...

2
@ Thorbjørn Ravn Andersen - Non sto dicendo che non sono d'accordo con te, lo so; <è più difensivo e i miei colleghi lo odiano se scrivo, !=quindi non lo faccio. Tuttavia, c'è un caso !=in C ++ ed è quello che ho presentato.

9
Uno scenario in cui si può finire con un extra accidentale i++è quando si cambia un ciclo while in un ciclo for. Non sono sicuro che avere la metà del numero di iterazioni sia comunque migliore di un ciclo (quasi) infinito; quest'ultimo probabilmente renderebbe più evidente il bug.
ak2,

12

L'uso di (i <10) è secondo me una pratica più sicura. Cattura il numero massimo di potenziali casi di dimissione - tutto ciò che è maggiore o uguale a 10. Confrontalo con l'altro caso (i! = 10); rileva solo un possibile caso di smettere di fumare - quando ho esattamente 10 anni.

Un sottoprodotto di questo è che migliora la leggibilità. Inoltre, se l'incremento dovrebbe essere diverso da 1, può aiutare a ridurre al minimo la probabilità di un problema nel caso in cui dovessimo commettere un errore durante la scrittura del caso di dimissione.

Ritenere:

1) for (i = 1; i < 14; i+=2)

2) for (i = 1; i != 14; i+=2)

Sebbene entrambi i casi siano probabilmente imperfetti / sbagliati, è probabile che il secondo sia PIÙ sbagliato in quanto non si chiuderà. Il primo caso si chiuderà e vi è una maggiore possibilità che esca nel punto giusto, anche se 14 è probabilmente il numero sbagliato (15 sarebbe probabilmente migliore).


Il primo caso potrebbe essere giusto! Se vuoi iterare su tutti i numeri naturali inferiori a 14, allora non c'è modo migliore per esprimerlo - calcolare il limite superiore "corretto" (13) sarebbe semplicemente stupido.
maaartinus,

9

Ecco un'altra risposta che nessuno sembra aver ancora trovato. fori loop devono essere utilizzati quando è necessario scorrere su una sequenza . L'utilizzo !=è il metodo più conciso per affermare la condizione di terminazione per il loop. Tuttavia, l'utilizzo di un operatore meno restrittivo è un linguaggio di programmazione difensiva molto comune. Per i numeri interi non importa: è solo una scelta personale senza un esempio più specifico. Passare in rassegna le raccolte con gli iteratori che si desidera utilizzare !=per le ragioni che altri hanno affermato. Se si considerano sequenze di floato double, si desidera evitare !=a tutti i costi.

Quello che volevo sottolineare è che forviene utilizzato quando è necessario iterare su una sequenza. La sequenza generata ha un punto iniziale, un intervallo e una condizione finale. Questi sono concisamente specificati nell'istruzione for. Se ti accorgi che (1) non include la porzione di passaggio di foro (2) specificando qualcosa come truela condizione di guardia, allora non dovresti usare un forloop!

Il whileciclo viene utilizzato per continuare l'elaborazione mentre viene soddisfatta una condizione specifica. Se non stai elaborando una sequenza, probabilmente vorrai whileinvece un ciclo. Gli argomenti della condizione di guardia sono simili qui, ma la decisione tra a whilee un forloop dovrebbe essere molto consapevole. Il whileciclo è sottovalutato nei circoli C ++ IMO.

Se stai elaborando una raccolta di elementi (un forutilizzo molto comune del loop), dovresti davvero utilizzare un metodo più specializzato. Sfortunatamente, std::for_eachè abbastanza doloroso in C ++ per una serie di ragioni. In molti casi, separare il corpo di un foranello in una funzione indipendente (sebbene un po 'dolorosa) si traduce in una soluzione molto più pulita. Quando si lavora con le collezioni, in considerazione std::for_each, std::transformo std::accumulate. L'implementazione di molti algoritmi diventa concisa e chiara quando espressa in questo modo. Per non parlare del fatto che isolare il corpo del loop in una funzione / metodo separato ti costringe a concentrarti sull'algoritmo, sui suoi requisiti di input e sui risultati.

Se stai usando Java, Python, Ruby o anche C ++ 0x , allora dovresti usare un ciclo foreach di raccolta appropriato . Man mano che i compilatori C ++ implementano questa funzione, un certo numero di forloop scompare così come questi tipi di discussioni.


2
"Tuttavia, l'utilizzo di un operatore meno restrittivo è un linguaggio di programmazione difensiva molto comune." Questo lo riassume più o meno.
James P.

+1 per discutere delle differenze di intento con il confronto con while, fore foreach(o (equivalente lingua)
Kevin Peno

A rigor di termini, il whileciclo viene utilizzato per continuare l'elaborazione fino a quando non viene soddisfatta una condizione specifica
Alnitak,

@Alnitak - D'oh ... lo aggiusterò :)
D.Shawley il

Sono stato confuso dai due possibili significati di "meno restrittivo": potrebbe riferirsi all'operatore indulgente nei valori che passa ( <) o all'operatore indulgente nei valori che non riesce ( !=).
Mateen Ulhaq,

4

L'uso di "minore di" è (solitamente) semanticamente corretto, in realtà intendi contare fino a quando inon è più inferiore a 10, quindi "minore di" trasmette chiaramente le tue intenzioni. L'uso di "non uguale" ovviamente funziona praticamente in casi call, ma trasmette un significato leggermente diverso. In alcuni casi questo può essere ciò di cui hai bisogno, ma nella mia esperienza non è mai stato così. Se hai davvero avuto un caso in cui ipotrebbe essere maggiore o minore di 10 ma vuoi continuare a fare loop fino a quando non è uguale a 10, allora quel codice avrebbe davvero bisogno di commentare molto chiaramente e probabilmente potrebbe essere meglio scritto con qualche altro costrutto, come come un whileciclo forse.


2

Uno dei motivi per cui preferirei un less thanover a not equalsè quello di agire come guardia. In alcune circostanze limitate (cattiva programmazione o sanificazione) si not equalspotrebbe saltare mentre less thansarebbe ancora in vigore.

Ecco un esempio in cui la mancanza di un controllo di sanificazione ha portato a risultati strani: http://www.michaeleisen.org/blog/?p=358


0

Ecco un motivo per cui potresti preferire l'utilizzo <piuttosto che !=. Se si utilizza un linguaggio con ambito variabile globale, cosa succede se si modifica un altro codice i? Se stai usando <piuttosto che !=, il peggio che succede è che l'iterazione termina più velocemente: forse alcuni altri incrementi di codice iper caso e salti alcune iterazioni nel ciclo for. Ma cosa succede se stai eseguendo il loop da 0 a 10, e il loop arriva a 9, e alcuni incrementi di thread scritti male iper qualche strano motivo. Quindi il ciclo termina tale iterazione e incrementi in imodo che il valore sia ora 11. Poiché il ciclo ha saltato la condizione di uscita ( imai uguale a 10), ora eseguirà il ciclo all'infinito.

Non deve necessariamente essere una logica di tipo threading e variabili globali particolarmente bizzarra che causa questo. Potrebbe essere solo che stai scrivendo un loop che deve tornare indietro. Se stai mutando iall'interno del loop e rovini la tua logica, averla in modo che abbia un limite superiore piuttosto che un !=è meno probabile che ti lasci in un loop infinito.


3
Naturalmente, questo sembra un argomento perfetto per ogni loop e uno stile di programmazione più funzionale in generale. Ma perché dovresti farlo quando le variabili mutabili sono molto più divertenti ?!
Tom Morris,

3
Non credo sia una ragione terribilmente valida. In entrambi i casi hai un bug che deve essere trovato e corretto, ma un ciclo infinito tende a rendere un bug piuttosto ovvio.
ak2,

0

Nel mondo incorporato, specialmente in ambienti rumorosi, non si può contare sulla RAM che si comporta necessariamente come dovrebbe. In un condizionale (per, mentre, se) in cui si confronta usando '==' o '! =' Si corre sempre il rischio che le variabili abbiano saltato quel valore cruciale che termina il ciclo - questo può avere conseguenze disastrose - Mars Lander conseguenze a livello. L'uso di "<" o ">" nella condizione fornisce un ulteriore livello di sicurezza per rilevare gli "sconosciuti sconosciuti".

Nell'esempio originale, se ifossero inspiegabilmente catapultati su un valore molto più grande di 10, il confronto "<" rileverebbe immediatamente l'errore ed uscirebbe dal ciclo, ma "! =" Continuerebbe a contare fino a quando non è ipassato tra 0 e ritorno a 10.


2
Un'altra variante correlata esiste con un codice come quello in for (i=4; i<length; i++) csum+=dat[i];cui i pacchetti legittimi avrebbero sempre un valore lengthdi almeno quattro, ma si potrebbero ricevere pacchetti danneggiati. Se tipi di pacchetti diversi hanno requisiti di lunghezza diversi, si potrebbe voler convalidare la lunghezza come parte dell'elaborazione che è diversa per ciascun tipo di pacchetto, ma convalidare prima il checksum come parte della logica comune. Se viene ricevuto un pacchetto di lunghezza inferiore, non importa se il calcolo del checksum riporta "buono" o "cattivo", ma non dovrebbe bloccarsi.
supercat
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.