Cela le rend plus lisible, et si vous passez i++à i+=2(par exemple), il fonctionnera très longtemps (ou peut-être pour toujours). Maintenant, puisque vous utilisez généralement <pour les cas où vous incrémentez l'itérateur de plus de 1, vous pouvez aussi bien utiliser <également pour le cas où vous l'incrémentez de 1 (pour la cohérence).
barak manos
112
5 != iest mauvais
Slava
27
Vous devez toujours être conscient de ce que vous faites, mais vous ferez quand même des erreurs. L'utilisation de <réduit la possibilité que quelqu'un introduise un bogue dans ce code.
Aurast
40
@OleTange Si vous itérez en utilisant des nombres à virgule flottante, vous êtes déjà foutu ..
CaptainCodeman
29
@Slava ... pas mal. Vous n'avez apparemment jamais été mordu en C par une simple faute de frappe faisant la différence entre if ( i == 5 ) ...et if ( i = 5 ). Des choses très différentes. L'inversion des opérandes évite le problème: puisque les littéraux ne le sont pas lvals, ils ne peuvent pas être affectés à et le compilateur lance sur la faute de frappe. @Zingam: bonne programmation défensive!
Nicholas Carey
Réponses:
303
while(time !=6:30pm){Work();}
Il est 18 h 31 ... Merde, ma prochaine chance de rentrer chez moi est demain! :)
Ceci pour montrer que la restriction plus forte atténue les risques et est probablement plus intuitive à comprendre.
Parfois, tenter de «réduire les risques» finit par cacher le bogue. Si la conception de la boucle doit empêcher le passage à 18h30 de passer inaperçu, alors l'utilisation <masquera le bogue, mais l'utilisation !=rendrait évident qu'il y a un problème.
Adrian McCarthy
27
@AdrianMcCarthy: Pas nécessairement; cela pourrait simplement introduire un deuxième bug, rendant le diagnostic plus difficile .
Courses de légèreté en orbite du
4
@AdrianMcCarthy: C'est un peu mon point; vous pourriez en avoir quelques exemples que vous n'avez pas encore vus;)
Courses de légèreté en orbite
6
Les itérateurs sont un parfait exemple du point @AdrianMcCarthy, je pense. La comparaison recommandée pour eux est! = Au lieu de <.
Taekahn
5
J'ai vu exactement cette erreur (et cela a causé 3 mois de débogage) - mais cette réponse manque complètement le point. Dans l'exemple donné, il est impossible de manquer la condition de fin. Vous pourriez même dire que choisir consciemment! = Au-dessus de <, c'est affirmer fortement ce fait. Atténuer un risque qui n'existe définitivement pas est une odeur de code pour moi. (Cela dit, vous pouvez également affirmer que <est idiomatique, que c'est une aussi bonne raison de l'utiliser que n'importe quelle autre.)
Ian Goldby
95
Il n'y a aucune raison technique. Mais il y a atténuation des risques, maintenabilité et meilleure compréhension du code.
<ou >sont des restrictions plus strictes !=et remplissent exactement le même objectif dans la plupart des cas (je dirais même dans tous les cas pratiques).
Il y a une question en double ici ; et une réponse intéressante .
Il existe certains types, tels que les itérateurs, qui ne prennent pas en charge <ou>, mais prennent en charge == et! =.
Simon B
1
C'est une boucle for avec int . Pas d'itérateurs.
Ely
7
"Mais il y a atténuation des risques, maintenabilité et meilleure compréhension du code." Ce sont, à proprement parler, des raisons techniques. :-)
TJ Crowder
@TJCrowder De quel type de raison s'agit-il lorsque vous voulez simplement parler de ce que les machines feront en conséquence?
Brilliand
1
@DanSheppard Je considérerais généralement l'atténuation du risque et la maintenabilité comme des raisons commerciales, et une meilleure compréhension du code comme une raison sociale (bien que ces raisons soient toutes appliquées à un problème technique dans ce cas). Les considérations «d'atténuation des risques» ne sont en aucun cas limitées aux questions techniques, et les considérations de «meilleure compréhension du code» peuvent changer si vous vous retrouvez à travailler avec un groupe de personnes différent (même si les machines impliquées ne changent pas du tout ).
Brilliand
85
Oui, il y a une raison. Si vous écrivez un (basé sur un ancien index simple) pour une boucle comme celle-ci
for(int i = a; i < b;++i){}
alors cela fonctionne comme prévu pour toutes les valeurs de aet b(c.-à-d. zéro itération quand a > bau lieu d'infini si vous l'aviez utilisé i == b;).
D'un autre côté, pour les itérateurs, vous écririez
for(auto it =begin; it !=end;++it)
car tout itérateur doit implémenter un operator!=, mais pas pour chaque itérateur, il est possible de fournir un operator<.
Également basé sur la plage pour les boucles
for(auto e : v)
ne sont pas seulement du sucre fantaisie, mais ils réduisent de manière mesurable les chances d'écrire un mauvais code.
Bel exemple. Je serais intéressé par un cas pour !=au lieu de <. Personnellement, je n'ai jamais utilisé ça, je pense.
Ely
@Elyasin Je n'en ai pas trouvé un bon, et je ne voulais pas en poster un mauvais: Imaginez que vous avez une sorte de conteneur circulaire, où vous augmentez de + x modulo la taille du conteneur N et vous voulez arrêter la boucle lorsque vous atteindre un certain indice .... c'est un très mauvais exemple. Peut-être que j'en trouverai un meilleur
idclev 463035818
La même boucle avec! = Au lieu de <indique clairement l'avantage de <(ou>) dans ce contexte. for (int i=a; i != b; i++) {}
Paul Smith
10
Utiliser quoi que ce soit d'autre !=avec les itérateurs est assez dangereux, car parfois les itérateurs peuvent être moins que comparés (c'est un pointeur par exemple) mais la comparaison n'a pas de sens (c'est une liste chaînée).
Zan Lynx
1
Apprenez à utiliser l'espace blanc, sérieusement.
Miles Rout
70
Vous pouvez avoir quelque chose comme
for(int i =0; i<5;++i){...if(...) i++;...}
Si votre variable de boucle est écrite par le code interne, la i!=5peut ne pas rompre cette boucle. Il est plus sûr de vérifier les inégalités.
Modifier la lisibilité. Le formulaire d'inégalité est beaucoup plus fréquemment utilisé. Par conséquent, c'est très rapide à lire car il n'y a rien de spécial à comprendre (la charge cérébrale est réduite car la tâche est courante). C'est donc cool pour les lecteurs de faire usage de ces habitudes.
Ce type de "if (condition) then i ++" est la première chose à laquelle j'ai pensé.
WernerCD
3
Entré en s'attendant à ce que ce soit la réponse la plus votée; J'ai été surpris d'avoir dû faire défiler aussi loin pour le trouver. Cela fait beaucoup plus pour convaincre un programmeur novice que "eh bien, vous devez utiliser la condition la plus forte". Les autres réponses, si elles restent au top, devraient incorporer cela comme une explication claire et évidente de ce qui peut mal tourner avec le code proposé par OP.
msouth
1
@msouth Je suis entièrement d'accord, mais mon POV est assez subjectif;)
johan d
3
@msouth J'adore quand un comportement de production inattendu expose mes bugs, n'est-ce pas? :-)
deworde
3
@msouth Regardez, tout ce que nous avons à faire est de ne jamais faire d'erreurs et tout ira bien. Simples.
deworde
48
Et enfin et surtout, cela s'appelle la programmation défensive , ce qui signifie de toujours prendre le cas le plus fort pour éviter les erreurs actuelles et futures qui influencent le programme.
Le seul cas où une programmation défensive n'est pas nécessaire est celui où les états ont été prouvés par des conditions préalables et postérieures (mais ensuite, prouver que c'est la plus défensive de toute la programmation).
Un bon exemple à l'appui de cela: la mémoire est vulnérable aux retournements de bits ( SEU ) causés par les radiations. Lorsque vous utilisez un intpour une i != 15condition, le compteur fonctionnera longtemps si quelque chose au-dessus du quatrième bit est inversé, en particulier sur les machines où sizeof(int)64 est. Les SEU sont une préoccupation très réelle à une altitude plus élevée ou dans l'espace (en raison d'un rayonnement plus élevé) , ou dans les grands supercalculateurs (car il y a tellement de mémoire).
Josh Sanford
2
Penser que votre programme fonctionnera bien lorsque le rayonnement change votre mémoire est une manière optimiste et une façon de penser anticatastrophique ... bon commentaire! :)
Luis Colorado
37
Je dirais qu'une expression comme
for(int i =0; i <100;++i ){...}
exprime plus l' intention que
for(int i =0; i !=100;++i ){...}
Le premier indique clairement que la condition est un test pour une limite supérieure exclusive sur une plage; ce dernier est un test binaire d'une condition de sortie. Et si le corps de la boucle n'est pas trivial, il peut ne pas apparaître que l'index n'est modifié que dans l' forinstruction elle-même.
"Je veux que cette boucle s'exécute au plus 5 fois" vs "Je veux exécuter cette boucle autant de fois que nécessaire, sauf exactement 5 fois, ce que je ne veux pas."
évêque le
+1 pour avoir mentionné la limite supérieure d'une plage. Je pense que cela est important pour les plages numériques. ! = ne définit pas une plage appropriée dans une boucle for comme celles
element11
22
Les itérateurs sont un cas important lorsque vous utilisez le plus souvent la !=notation:
for(auto it = vector.begin(); it != vector.end();++it){// do stuff}
Accordé: en pratique, j'écrirais la même chose en s'appuyant sur range-for:
for(auto& item : vector){// do stuff}
mais le point demeure: on compare normalement les itérateurs en utilisant ==ou !=.
Les itérateurs n'ont souvent qu'une notion d'égalité / inégalité et non un ordre, c'est pourquoi vous les utilisez !=pour eux. Par exemple, dans un que std::vectorvous pourriez utiliser <car l'itérateur est lié à l'index / pointeur, mais dans un std::setil n'y a pas un tel ordre strict, car il parcourt un arbre binaire.
Martin C.
19
La condition de boucle est un invariant de boucle forcée.
Supposons que vous ne regardiez pas le corps de la boucle:
for(int i =0; i !=5;++i){// ?}
dans ce cas, vous savez au début de l'itération de boucle qui in'est pas égale 5.
for(int i =0; i <5;++i){// ?}
dans ce cas, vous savez au début de l'itération de boucle qui iest inférieure à 5.
La seconde est beaucoup, beaucoup plus d'informations que la première, non? Maintenant, l'intention du programmeur est (presque certainement) la même, mais si vous recherchez des bogues, avoir confiance en lisant une ligne de code est une bonne chose. Et le second applique cet invariant, ce qui signifie que certains bogues qui vous mordraient dans le premier cas ne peuvent tout simplement pas se produire (ou ne causent pas de corruption de mémoire, par exemple) dans le second cas.
Vous en savez plus sur l'état du programme, en lisant moins de code, avec <qu'avec!= . Et sur les processeurs modernes, ils prennent autant de temps que de différence.
Si votre in'a pas été manipulé dans le corps de la boucle, et il a toujours été augmenté de 1, et il a commencé moins de5 , il n'y aurait pas de différence. Mais pour savoir s'il a été manipulé, vous devez confirmer chacun de ces faits.
Certains de ces faits sont relativement faciles, mais vous pouvez vous tromper. Vérifier tout le corps de la boucle est cependant une douleur.
En C ++, vous pouvez écrire un indexestype tel que:
for(constint i : indexes(0,5)){// ?}
fait la même chose que l'une des deux forboucles ci-dessus , même jusqu'à ce que le compilateur l'optimise avec le même code. Ici, cependant, vous savez que icela ne peut pas être manipulé dans le corps de la boucle, comme il est déclaré const, sans que le code ne corrompe la mémoire.
Plus vous pouvez obtenir d'informations sur une ligne de code sans avoir à comprendre le contexte, plus il est facile de rechercher ce qui ne va pas. <dans le cas de boucles entières, vous donne plus d'informations sur l'état du code sur cette ligne que !=ne le fait.
Il peut arriver que la variable isoit définie sur une grande valeur et si vous utilisez simplement l' !=opérateur, vous vous retrouverez dans une boucle sans fin.
Pas vraiment sans fin - sur la plupart des implémentations de C, ise terminera silencieusement quand à son maximum. Mais oui, probablement plus longtemps que ce que vous êtes prêt à attendre.
usr2564301
12
Comme vous pouvez le voir dans les nombreuses autres réponses, il y a des raisons d'utiliser <au lieu de! = Qui aidera dans les cas de bord, les conditions initiales, la modification involontaire du compteur de boucles, etc.
Honnêtement, je ne pense pas que vous puissiez insister suffisamment sur l'importance de la convention. Pour cet exemple, il sera assez facile pour les autres programmeurs de voir ce que vous essayez de faire, mais cela provoquera une double prise. L'un des emplois de la programmation est de le rendre aussi lisible et familier que possible pour tout le monde, donc inévitablement lorsque quelqu'un doit mettre à jour / modifier votre code, cela ne prend pas beaucoup d'efforts pour comprendre ce que vous faisiez dans différents blocs de code. . Si je voyais quelqu'un utiliser !=, je suppose qu'il y avait une raison pour laquelle ils l'ont utilisé à la place <et si c'était une grande boucle, je regarderais tout cela en essayant de comprendre ce que vous avez fait qui rendait cela nécessaire ... et c'est temps perdu.
Comme déjà dit par Ian Newson, vous ne pouvez pas boucler de manière fiable sur une variable flottante et quitter avec !=. Par exemple,
for(double x=0; x!=1; x+=0.1){}
sera en fait bouclé pour toujours, car 0,1 ne peut pas être représenté exactement en virgule flottante, donc le compteur manque de peu 1. Avec < cela, il se termine.
(Notez cependant qu'il s'agit d' un comportement fondamentalement indéfini, que vous obteniez 0,9999 ... comme dernier nombre accepté - ce qui viole l'hypothèse inférieure à - ou que vous quittez déjà à 1,0000000000000001.)
Je prends l'adjectif «technique» pour désigner le comportement / les bizarreries du langage et les effets secondaires du compilateur tels que les performances du code généré.
À cette fin, la réponse est: non (*). Le (*) est "veuillez consulter le manuel de votre processeur". Si vous travaillez avec un système RISC ou FPGA à boîtier périphérique, vous devrez peut-être vérifier quelles instructions sont générées et ce qu'elles coûtent. Mais si vous utilisez à peu près une architecture moderne classique, alors il n'y a pas de différence de niveau de processeur important entre le coût lt, eq, neet gt.
Si vous utilisez un cas limite , vous pouvez constater que !=nécessite trois opérations ( cmp, not, beq) contre deux ( cmp, blt xtr myo). Encore une fois, RTM dans ce cas.
Pour la plupart, les raisons sont défensives / durcies, en particulier lorsque vous travaillez avec des pointeurs ou des boucles complexes. Considérer
Un exemple moins artificiel serait où vous utilisez des valeurs de retour pour effectuer des incréments, en acceptant des données d'un utilisateur:
#include<iostream>int main(){size_t len =5, step;for(size_t i =0; i != len;){
std::cout <<"i = "<< i <<", step? "<< std::flush;
std::cin >> step;
i += step;// here for emphasis, it could go in the for(;;)}}
Essayez ceci et entrez les valeurs 1, 2, 10, 999.
Vous pouvez empêcher cela:
#include<iostream>int main(){size_t len =5, step;for(size_t i =0; i != len;){
std::cout <<"i = "<< i <<", step? "<< std::flush;
std::cin >> step;if(step + i > len)
std::cout <<"too much.\n";else
i += step;}}
Mais ce que vous vouliez probablement était
#include<iostream>int main(){size_t len =5, step;for(size_t i =0; i < len;){
std::cout <<"i = "<< i <<", step? "<< std::flush;
std::cin >> step;
i += step;}}
Il existe également une sorte de parti pris conventionnel <, car la commande dans des conteneurs standard repose souvent sur operator<, par exemple le hachage dans plusieurs conteneurs STL détermine l'égalité en disant
Pour plus de lisibilité, i + = l'étape doit se trouver dans la partie contrôle. Dans le cas où vous feriez un pas = 0
Mikkel Christiansen
1
Bien que cela puisse aider à la lisibilité d'un bon code, je voulais souligner la faille dans la version erronée
kfsone
9
Il y a plusieurs façons d'écrire n'importe quel type de code (généralement), il se trouve qu'il y a juste deux façons dans ce cas (trois si vous comptez <= et> =).
Dans ce cas, les gens préfèrent> et <pour s'assurer que même si quelque chose d'inattendu se produit dans la boucle (comme un bug), il ne bouclera pas à l'infini (BAD). Considérez le code suivant, par exemple.
for(int i =1; i !=3; i++){//More Code
i =5;//OOPS! MISTAKE!//More Code}
Si nous utilisions (i <3), nous serions à l'abri d'une boucle infinie car elle placerait une plus grande restriction.
C'est vraiment votre choix si vous voulez une erreur dans votre programme pour arrêter le tout ou continuer à fonctionner avec le bogue là-bas.
on pourrait argumenter que la boucle infinie serait un bon indicateur de l'erreur, mais alors un autre dirait que i = 5 n'est pas nécessairement une erreur
njzk2
7
La raison la plus courante d'utiliser < est la convention. De plus en plus de programmeurs considèrent les boucles comme celle-ci comme "pendant que l'index est dans la plage" plutôt que "jusqu'à ce que l'index atteigne la fin". Il y a de la valeur à respecter les conventions quand vous le pouvez.
D'un autre côté, de nombreuses réponses affirment que l'utilisation du <formulaire permet d'éviter les bugs. Je dirais que dans de nombreux cas, cela aide simplement à cacher les bogues. Si l'index de boucle est censé atteindre la valeur finale, et au lieu de cela, il va au-delà, il se passe quelque chose auquel vous ne vous attendiez pas qui pourrait provoquer un dysfonctionnement (ou être un effet secondaire d'un autre bogue). Le <retardera probablement la découverte du bug. Il !=est plus susceptible de provoquer un décrochage, un blocage ou même un crash, ce qui vous aidera à repérer le bug plus tôt. Plus tôt un bogue est détecté, moins il est coûteux de le corriger.
Notez que cette convention est particulière à l'indexation des tableaux et des vecteurs. Lorsque vous parcourez presque tout autre type de structure de données, vous utiliseriez un itérateur (ou un pointeur) et vérifieriez directement une valeur finale. Dans ces cas, vous devez être sûr que l'itérateur atteindra et ne dépassera pas la valeur finale réelle.
Par exemple, si vous parcourez une chaîne C simple, il est généralement plus courant d'écrire:
for(char*p = foo;*p !='\0';++p){// do something with *p}
que
int length = strlen(foo);for(int i =0; i < length;++i){// do something with foo[i]}
D'une part, si la chaîne est très longue, la deuxième forme sera plus lente car strlenc'est une autre passe à travers la chaîne.
Avec une chaîne C ++ std ::, vous utiliseriez une boucle for basée sur une plage, un algorithme standard ou des itérateurs, même si la longueur est facilement disponible. Si vous utilisez des itérateurs, la convention est d'utiliser !=plutôt que <, comme dans:
for(auto it = foo.begin(); it != foo.end();++it){...}
De même, itérer un arbre ou une liste ou un deque implique généralement de rechercher un pointeur nul ou une autre sentinelle plutôt que de vérifier si un index reste dans une plage.
Vous avez tout à fait raison de souligner l'importance de l'idiome dans la programmation. Si je regarde du code et qu'il a un modèle familier, je n'ai pas besoin de perdre de temps à déduire le modèle, mais je peux aller directement au cœur de celui-ci. Je suppose que c'est ma principale prise en main avec les comparaisons Yoda - elles ne semblent pas idiomatiques, donc je dois les lire deux fois pour être sûr qu'elles signifient ce que je pense qu'elles signifient.
Toby Speight
7
Une raison de ne pas utiliser cette construction est les nombres à virgule flottante. !=est une comparaison très dangereuse à utiliser avec des flotteurs car elle sera rarement évaluée comme vraie même si les chiffres se ressemblent. <ou >supprime ce risque.
Si votre itérateur de boucle est un nombre à virgule flottante, alors le !=versus <est le moindre de vos problèmes.
Lundin
7
Il y a deux raisons liées à suivre cette pratique qui ont toutes deux à voir avec le fait qu'un langage de programmation est, après tout, un langage qui sera lu par les humains (entre autres).
(1) Un peu de redondance. En langage naturel, nous fournissons généralement plus d'informations que ce qui est strictement nécessaire, un peu comme un code de correction d'erreur. Ici, les informations supplémentaires sont que la variable de boucle i(voyez comment j'ai utilisé la redondance ici? Si vous ne saviez pas ce que signifie «variable de boucle», ou si vous avez oublié le nom de la variable, après avoir lu «variable de boucle i», vous avez le plein informations) est inférieur à 5 pendant la boucle, et pas seulement différent de 5. La redondance améliore la lisibilité.
(2) Convention. Les langues ont des manières standard spécifiques d'exprimer certaines situations. Si vous ne suivez pas la façon établie de dire quelque chose, vous serez toujours compris, mais l'effort pour le destinataire de votre message est plus important car certaines optimisations ne fonctionneront pas. Exemple:
Ne parlez pas de la purée chaude. Illuminez simplement la difficulté!
La première phrase est une traduction littérale d'un idiome allemand. Le second est un idiome anglais courant avec les mots principaux remplacés par des synonymes. Le résultat est compréhensible mais prend beaucoup plus de temps à comprendre que cela:
Ne tourne pas autour du pot. Expliquez simplement le problème!
Cela est vrai même si les synonymes utilisés dans la première version correspondent mieux à la situation que les mots conventionnels dans l'idiome anglais. Des forces similaires sont en vigueur lorsque les programmeurs lisent du code. C'est aussi pourquoi 5 != iet 5 > isont des façons étranges de le mettre à moins que vous ne travailliez dans un environnement dans lequel il est standard d'échanger le plus normal i != 5et i < 5de cette manière. De telles communautés de dialectes existent, probablement parce que la cohérence facilite la mémorisation de l'écriture 5 == iau lieu du naturel mais sujet aux erreurs i == 5.
Ausgezeichnet . De même, on n'écrirait pas le test comme i < 17+(36/-3)(même quand ce sont des constantes utilisées ailleurs; alors vous devez écrire leurs noms pour plus de clarté!).
usr2564301
6
L'utilisation de comparaisons relationnelles dans de tels cas est plus une habitude populaire qu'autre chose. Il a gagné en popularité à l'époque où des considérations conceptuelles telles que les catégories d'itérateurs et leur comparabilité n'étaient pas considérées comme hautement prioritaires.
Je dirais que l'on devrait préférer utiliser des comparaisons d'égalité plutôt que des comparaisons relationnelles dans la mesure du possible, car les comparaisons d'égalité imposent moins d'exigences aux valeurs comparées. Être EqualityComparable est une exigence moindre que d'être LessThanComparable .
Un autre exemple qui démontre l'applicabilité plus large de la comparaison de l'égalité dans de tels contextes est l'énigme populaire avec la mise en œuvre de l' unsigneditération jusqu'à 0. Cela peut être fait comme
for(unsigned i =42; i !=-1;--i)...
Notez que ce qui précède s'applique également à l'itération signée et non signée, tandis que la version relationnelle se décompose en types non signés.
Outre les exemples, où la variable de boucle changera (involontairement) à l'intérieur du corps, il existe d'autres raisons d'utiliser les opérateurs plus petits que ou plus grands que:
Les négations rendent le code plus difficile à comprendre
La deuxième puce est au mieux une raison frivole. Le code doit être correct et clair. Compact est beaucoup moins important.
Jonathan Leffler
@JonathanLeffler Eh bien, dans un TDD REPL, ce caractère supplémentaire est une nano-seconde supplémentaire pour l'analyse, et multiplie par un milliard d'itérations pour trouver le bogue causé par l'écriture 5 != $i, qui n'a pris qu'une seconde entière de ma vie. Euh ... ricanement
évêque le
1
En plus des diverses personnes qui ont mentionné qu'il atténue les risques, il réduit également le nombre de surcharges de fonctions nécessaires pour interagir avec divers composants de bibliothèque standard. Par exemple, si vous souhaitez que votre type soit stockable dans un std::set, ou utilisé comme clé pour std::map, ou utilisé avec certains des algorithmes de recherche et de tri, la bibliothèque standard utilise généralement std::lesspour comparer les objets car la plupart des algorithmes n'ont besoin que d'un ordre faible strict . Ainsi, il devient une bonne habitude d'utiliser les <comparaisons au lieu de !=comparaisons (où cela a du sens, bien sûr).
vous pouvez avoir raison sur le nombre de surcharges, mais lorsque l'on considère les exigences sur les objets, alors pour presque n'importe quel objet on peut définir un !=opérateur, mais il n'est pas toujours possible de définir un ordre faible strict. En ce sens, ce !=serait mieux, car cela impose moins d'exigences au type. Cependant, je reste avec le <.
idclev 463035818
0
Il n'y a pas de problème d'un point de vue syntaxique, mais la logique derrière cette expression 5!=in'est pas solide.
À mon avis, utiliser !=pour définir les limites d'une boucle for n'est pas logique, car une boucle for incrémente ou diminue l'index d'itération, donc définir la boucle pour itérer jusqu'à ce que l'index d'itération devienne hors limites ( !=vers quelque chose) n'est pas un bonne mise en œuvre.
Il fonctionnera, mais elle est sujette à la mauvaise conduite depuis le traitement des données limite est perdue lors de l' utilisation !=d'un problème incrémental (ce qui signifie que vous savez dès le début si elle incréments ou décréments), c'est pourquoi , au lieu de !=l' <>>==>sont utilisés.
La logique est parfaitement fine. Quand ise 5termine la boucle. Vouliez-vous dire autre chose?
Dan Getz
1
Si les données ne sont pas transférées correctement à cause de la chaleur, les influences électro-magnétiques de votre code s'exécutent pour toujours. Si vous faites cela sur un poste de travail ou un serveur ordinaire avec RAM ECC, il n'y a pas de problème (ni logique, ni technique, ni physique)
i++
ài+=2
(par exemple), il fonctionnera très longtemps (ou peut-être pour toujours). Maintenant, puisque vous utilisez généralement<
pour les cas où vous incrémentez l'itérateur de plus de 1, vous pouvez aussi bien utiliser<
également pour le cas où vous l'incrémentez de 1 (pour la cohérence).5 != i
est mauvaisif ( i == 5 ) ...
etif ( i = 5 )
. Des choses très différentes. L'inversion des opérandes évite le problème: puisque les littéraux ne le sont paslvals
, ils ne peuvent pas être affectés à et le compilateur lance sur la faute de frappe. @Zingam: bonne programmation défensive!Réponses:
Il est 18 h 31 ... Merde, ma prochaine chance de rentrer chez moi est demain! :)
Ceci pour montrer que la restriction plus forte atténue les risques et est probablement plus intuitive à comprendre.
la source
<
masquera le bogue, mais l'utilisation!=
rendrait évident qu'il y a un problème.Il n'y a aucune raison technique. Mais il y a atténuation des risques, maintenabilité et meilleure compréhension du code.
<
ou>
sont des restrictions plus strictes!=
et remplissent exactement le même objectif dans la plupart des cas (je dirais même dans tous les cas pratiques).Il y a une question en double ici ; et une réponse intéressante .
la source
Oui, il y a une raison. Si vous écrivez un (basé sur un ancien index simple) pour une boucle comme celle-ci
alors cela fonctionne comme prévu pour toutes les valeurs de
a
etb
(c.-à-d. zéro itération quanda > b
au lieu d'infini si vous l'aviez utiliséi == b;
).D'un autre côté, pour les itérateurs, vous écririez
car tout itérateur doit implémenter un
operator!=
, mais pas pour chaque itérateur, il est possible de fournir unoperator<
.Également basé sur la plage pour les boucles
ne sont pas seulement du sucre fantaisie, mais ils réduisent de manière mesurable les chances d'écrire un mauvais code.
la source
!=
au lieu de<
. Personnellement, je n'ai jamais utilisé ça, je pense.for (int i=a; i != b; i++) {}
!=
avec les itérateurs est assez dangereux, car parfois les itérateurs peuvent être moins que comparés (c'est un pointeur par exemple) mais la comparaison n'a pas de sens (c'est une liste chaînée).Vous pouvez avoir quelque chose comme
Si votre variable de boucle est écrite par le code interne, la
i!=5
peut ne pas rompre cette boucle. Il est plus sûr de vérifier les inégalités.Modifier la lisibilité. Le formulaire d'inégalité est beaucoup plus fréquemment utilisé. Par conséquent, c'est très rapide à lire car il n'y a rien de spécial à comprendre (la charge cérébrale est réduite car la tâche est courante). C'est donc cool pour les lecteurs de faire usage de ces habitudes.
la source
Et enfin et surtout, cela s'appelle la programmation défensive , ce qui signifie de toujours prendre le cas le plus fort pour éviter les erreurs actuelles et futures qui influencent le programme.
Le seul cas où une programmation défensive n'est pas nécessaire est celui où les états ont été prouvés par des conditions préalables et postérieures (mais ensuite, prouver que c'est la plus défensive de toute la programmation).
la source
int
pour unei != 15
condition, le compteur fonctionnera longtemps si quelque chose au-dessus du quatrième bit est inversé, en particulier sur les machines oùsizeof(int)
64 est. Les SEU sont une préoccupation très réelle à une altitude plus élevée ou dans l'espace (en raison d'un rayonnement plus élevé) , ou dans les grands supercalculateurs (car il y a tellement de mémoire).Je dirais qu'une expression comme
exprime plus l' intention que
Le premier indique clairement que la condition est un test pour une limite supérieure exclusive sur une plage; ce dernier est un test binaire d'une condition de sortie. Et si le corps de la boucle n'est pas trivial, il peut ne pas apparaître que l'index n'est modifié que dans l'
for
instruction elle-même.la source
Les itérateurs sont un cas important lorsque vous utilisez le plus souvent la
!=
notation:Accordé: en pratique, j'écrirais la même chose en s'appuyant sur
range-for
:mais le point demeure: on compare normalement les itérateurs en utilisant
==
ou!=
.la source
!=
pour eux. Par exemple, dans un questd::vector
vous pourriez utiliser<
car l'itérateur est lié à l'index / pointeur, mais dans unstd::set
il n'y a pas un tel ordre strict, car il parcourt un arbre binaire.La condition de boucle est un invariant de boucle forcée.
Supposons que vous ne regardiez pas le corps de la boucle:
dans ce cas, vous savez au début de l'itération de boucle qui
i
n'est pas égale5
.dans ce cas, vous savez au début de l'itération de boucle qui
i
est inférieure à5
.La seconde est beaucoup, beaucoup plus d'informations que la première, non? Maintenant, l'intention du programmeur est (presque certainement) la même, mais si vous recherchez des bogues, avoir confiance en lisant une ligne de code est une bonne chose. Et le second applique cet invariant, ce qui signifie que certains bogues qui vous mordraient dans le premier cas ne peuvent tout simplement pas se produire (ou ne causent pas de corruption de mémoire, par exemple) dans le second cas.
Vous en savez plus sur l'état du programme, en lisant moins de code, avec
<
qu'avec!=
. Et sur les processeurs modernes, ils prennent autant de temps que de différence.Si votre
i
n'a pas été manipulé dans le corps de la boucle, et il a toujours été augmenté de 1, et il a commencé moins de5
, il n'y aurait pas de différence. Mais pour savoir s'il a été manipulé, vous devez confirmer chacun de ces faits.Certains de ces faits sont relativement faciles, mais vous pouvez vous tromper. Vérifier tout le corps de la boucle est cependant une douleur.
En C ++, vous pouvez écrire un
indexes
type tel que:fait la même chose que l'une des deux
for
boucles ci-dessus , même jusqu'à ce que le compilateur l'optimise avec le même code. Ici, cependant, vous savez quei
cela ne peut pas être manipulé dans le corps de la boucle, comme il est déclaréconst
, sans que le code ne corrompe la mémoire.Plus vous pouvez obtenir d'informations sur une ligne de code sans avoir à comprendre le contexte, plus il est facile de rechercher ce qui ne va pas.
<
dans le cas de boucles entières, vous donne plus d'informations sur l'état du code sur cette ligne que!=
ne le fait.la source
Oui; OpenMP ne met pas en parallèle les boucles avec la
!=
condition.la source
Il peut arriver que la variable
i
soit définie sur une grande valeur et si vous utilisez simplement l'!=
opérateur, vous vous retrouverez dans une boucle sans fin.la source
i
se terminera silencieusement quand à son maximum. Mais oui, probablement plus longtemps que ce que vous êtes prêt à attendre.Comme vous pouvez le voir dans les nombreuses autres réponses, il y a des raisons d'utiliser <au lieu de! = Qui aidera dans les cas de bord, les conditions initiales, la modification involontaire du compteur de boucles, etc.
Honnêtement, je ne pense pas que vous puissiez insister suffisamment sur l'importance de la convention. Pour cet exemple, il sera assez facile pour les autres programmeurs de voir ce que vous essayez de faire, mais cela provoquera une double prise. L'un des emplois de la programmation est de le rendre aussi lisible et familier que possible pour tout le monde, donc inévitablement lorsque quelqu'un doit mettre à jour / modifier votre code, cela ne prend pas beaucoup d'efforts pour comprendre ce que vous faisiez dans différents blocs de code. . Si je voyais quelqu'un utiliser
!=
, je suppose qu'il y avait une raison pour laquelle ils l'ont utilisé à la place<
et si c'était une grande boucle, je regarderais tout cela en essayant de comprendre ce que vous avez fait qui rendait cela nécessaire ... et c'est temps perdu.la source
Comme déjà dit par Ian Newson, vous ne pouvez pas boucler de manière fiable sur une variable flottante et quitter avec
!=
. Par exemple,sera en fait bouclé pour toujours, car 0,1 ne peut pas être représenté exactement en virgule flottante, donc le compteur manque de peu 1. Avec
<
cela, il se termine.(Notez cependant qu'il s'agit d' un comportement fondamentalement indéfini, que vous obteniez 0,9999 ... comme dernier nombre accepté - ce qui viole l'hypothèse inférieure à - ou que vous quittez déjà à 1,0000000000000001.)
la source
Je prends l'adjectif «technique» pour désigner le comportement / les bizarreries du langage et les effets secondaires du compilateur tels que les performances du code généré.
À cette fin, la réponse est: non (*). Le (*) est "veuillez consulter le manuel de votre processeur". Si vous travaillez avec un système RISC ou FPGA à boîtier périphérique, vous devrez peut-être vérifier quelles instructions sont générées et ce qu'elles coûtent. Mais si vous utilisez à peu près une architecture moderne classique, alors il n'y a pas de différence de niveau de processeur important entre le coût
lt
,eq
,ne
etgt
.Si vous utilisez un cas limite , vous pouvez constater que
!=
nécessite trois opérations (cmp
,not
,beq
) contre deux (cmp
,blt xtr myo
). Encore une fois, RTM dans ce cas.Pour la plupart, les raisons sont défensives / durcies, en particulier lorsque vous travaillez avec des pointeurs ou des boucles complexes. Considérer
Un exemple moins artificiel serait où vous utilisez des valeurs de retour pour effectuer des incréments, en acceptant des données d'un utilisateur:
Essayez ceci et entrez les valeurs 1, 2, 10, 999.
Vous pouvez empêcher cela:
Mais ce que vous vouliez probablement était
Il existe également une sorte de parti pris conventionnel
<
, car la commande dans des conteneurs standard repose souvent suroperator<
, par exemple le hachage dans plusieurs conteneurs STL détermine l'égalité en disantSi
lhs
etrhs
sont une classe définie par l'utilisateur écrivant ce code commeL'implémenteur doit fournir deux fonctions de comparaison. Ainsi
<
est devenu l'opérateur privilégié.la source
Il y a plusieurs façons d'écrire n'importe quel type de code (généralement), il se trouve qu'il y a juste deux façons dans ce cas (trois si vous comptez <= et> =).
Dans ce cas, les gens préfèrent> et <pour s'assurer que même si quelque chose d'inattendu se produit dans la boucle (comme un bug), il ne bouclera pas à l'infini (BAD). Considérez le code suivant, par exemple.
Si nous utilisions (i <3), nous serions à l'abri d'une boucle infinie car elle placerait une plus grande restriction.
C'est vraiment votre choix si vous voulez une erreur dans votre programme pour arrêter le tout ou continuer à fonctionner avec le bogue là-bas.
J'espère que cela vous a aidé!
la source
La raison la plus courante d'utiliser
<
est la convention. De plus en plus de programmeurs considèrent les boucles comme celle-ci comme "pendant que l'index est dans la plage" plutôt que "jusqu'à ce que l'index atteigne la fin". Il y a de la valeur à respecter les conventions quand vous le pouvez.D'un autre côté, de nombreuses réponses affirment que l'utilisation du
<
formulaire permet d'éviter les bugs. Je dirais que dans de nombreux cas, cela aide simplement à cacher les bogues. Si l'index de boucle est censé atteindre la valeur finale, et au lieu de cela, il va au-delà, il se passe quelque chose auquel vous ne vous attendiez pas qui pourrait provoquer un dysfonctionnement (ou être un effet secondaire d'un autre bogue). Le<
retardera probablement la découverte du bug. Il!=
est plus susceptible de provoquer un décrochage, un blocage ou même un crash, ce qui vous aidera à repérer le bug plus tôt. Plus tôt un bogue est détecté, moins il est coûteux de le corriger.Notez que cette convention est particulière à l'indexation des tableaux et des vecteurs. Lorsque vous parcourez presque tout autre type de structure de données, vous utiliseriez un itérateur (ou un pointeur) et vérifieriez directement une valeur finale. Dans ces cas, vous devez être sûr que l'itérateur atteindra et ne dépassera pas la valeur finale réelle.
Par exemple, si vous parcourez une chaîne C simple, il est généralement plus courant d'écrire:
que
D'une part, si la chaîne est très longue, la deuxième forme sera plus lente car
strlen
c'est une autre passe à travers la chaîne.Avec une chaîne C ++ std ::, vous utiliseriez une boucle for basée sur une plage, un algorithme standard ou des itérateurs, même si la longueur est facilement disponible. Si vous utilisez des itérateurs, la convention est d'utiliser
!=
plutôt que<
, comme dans:De même, itérer un arbre ou une liste ou un deque implique généralement de rechercher un pointeur nul ou une autre sentinelle plutôt que de vérifier si un index reste dans une plage.
la source
Une raison de ne pas utiliser cette construction est les nombres à virgule flottante.
!=
est une comparaison très dangereuse à utiliser avec des flotteurs car elle sera rarement évaluée comme vraie même si les chiffres se ressemblent.<
ou>
supprime ce risque.la source
!=
versus<
est le moindre de vos problèmes.Il y a deux raisons liées à suivre cette pratique qui ont toutes deux à voir avec le fait qu'un langage de programmation est, après tout, un langage qui sera lu par les humains (entre autres).
(1) Un peu de redondance. En langage naturel, nous fournissons généralement plus d'informations que ce qui est strictement nécessaire, un peu comme un code de correction d'erreur. Ici, les informations supplémentaires sont que la variable de boucle
i
(voyez comment j'ai utilisé la redondance ici? Si vous ne saviez pas ce que signifie «variable de boucle», ou si vous avez oublié le nom de la variable, après avoir lu «variable de bouclei
», vous avez le plein informations) est inférieur à 5 pendant la boucle, et pas seulement différent de 5. La redondance améliore la lisibilité.(2) Convention. Les langues ont des manières standard spécifiques d'exprimer certaines situations. Si vous ne suivez pas la façon établie de dire quelque chose, vous serez toujours compris, mais l'effort pour le destinataire de votre message est plus important car certaines optimisations ne fonctionneront pas. Exemple:
La première phrase est une traduction littérale d'un idiome allemand. Le second est un idiome anglais courant avec les mots principaux remplacés par des synonymes. Le résultat est compréhensible mais prend beaucoup plus de temps à comprendre que cela:
Cela est vrai même si les synonymes utilisés dans la première version correspondent mieux à la situation que les mots conventionnels dans l'idiome anglais. Des forces similaires sont en vigueur lorsque les programmeurs lisent du code. C'est aussi pourquoi
5 != i
et5 > i
sont des façons étranges de le mettre à moins que vous ne travailliez dans un environnement dans lequel il est standard d'échanger le plus normali != 5
eti < 5
de cette manière. De telles communautés de dialectes existent, probablement parce que la cohérence facilite la mémorisation de l'écriture5 == i
au lieu du naturel mais sujet aux erreursi == 5
.la source
i < 17+(36/-3)
(même quand ce sont des constantes utilisées ailleurs; alors vous devez écrire leurs noms pour plus de clarté!).L'utilisation de comparaisons relationnelles dans de tels cas est plus une habitude populaire qu'autre chose. Il a gagné en popularité à l'époque où des considérations conceptuelles telles que les catégories d'itérateurs et leur comparabilité n'étaient pas considérées comme hautement prioritaires.
Je dirais que l'on devrait préférer utiliser des comparaisons d'égalité plutôt que des comparaisons relationnelles dans la mesure du possible, car les comparaisons d'égalité imposent moins d'exigences aux valeurs comparées. Être EqualityComparable est une exigence moindre que d'être LessThanComparable .
Un autre exemple qui démontre l'applicabilité plus large de la comparaison de l'égalité dans de tels contextes est l'énigme populaire avec la mise en œuvre de l'
unsigned
itération jusqu'à0
. Cela peut être fait commeNotez que ce qui précède s'applique également à l'itération signée et non signée, tandis que la version relationnelle se décompose en types non signés.
la source
Outre les exemples, où la variable de boucle changera (involontairement) à l'intérieur du corps, il existe d'autres raisons d'utiliser les opérateurs plus petits que ou plus grands que:
<
ou>
est un seul caractère, mais!=
deuxla source
5 != $i
, qui n'a pris qu'une seconde entière de ma vie. Euh ... ricanementEn plus des diverses personnes qui ont mentionné qu'il atténue les risques, il réduit également le nombre de surcharges de fonctions nécessaires pour interagir avec divers composants de bibliothèque standard. Par exemple, si vous souhaitez que votre type soit stockable dans un
std::set
, ou utilisé comme clé pourstd::map
, ou utilisé avec certains des algorithmes de recherche et de tri, la bibliothèque standard utilise généralementstd::less
pour comparer les objets car la plupart des algorithmes n'ont besoin que d'un ordre faible strict . Ainsi, il devient une bonne habitude d'utiliser les<
comparaisons au lieu de!=
comparaisons (où cela a du sens, bien sûr).la source
!=
opérateur, mais il n'est pas toujours possible de définir un ordre faible strict. En ce sens, ce!=
serait mieux, car cela impose moins d'exigences au type. Cependant, je reste avec le<
.Il n'y a pas de problème d'un point de vue syntaxique, mais la logique derrière cette expression
5!=i
n'est pas solide.À mon avis, utiliser
!=
pour définir les limites d'une boucle for n'est pas logique, car une boucle for incrémente ou diminue l'index d'itération, donc définir la boucle pour itérer jusqu'à ce que l'index d'itération devienne hors limites (!=
vers quelque chose) n'est pas un bonne mise en œuvre.Il fonctionnera, mais elle est sujette à la mauvaise conduite depuis le traitement des données limite est perdue lors de l' utilisation
!=
d'un problème incrémental (ce qui signifie que vous savez dès le début si elle incréments ou décréments), c'est pourquoi , au lieu de!=
l'<>>==>
sont utilisés.la source
i
se5
termine la boucle. Vouliez-vous dire autre chose?