Y a-t-il une raison technique d'utiliser> (<) au lieu de! = Lors de l'incrémentation de 1 dans une boucle 'for'?

198

Je ne vois presque jamais une forboucle comme celle-ci:

for (int i = 0; 5 != i; ++i)
{}

Y a-t-il une raison technique à utiliser >ou à la <place de l' !=incrémentation de 1 dans une forboucle? Ou c'est plus une convention?

Zingam
la source
104
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.

Antonio
la source
25
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 .

Ely
la source
6
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.

idclev 463035818
la source
4
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.

johan d
la source
6
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).

Paul Ogilvie
la source
2
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.

Nicholas Carey
la source
13
"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 !=.

Escualo
la source
2
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( const int 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.

Yakk - Adam Nevraumont
la source
13

Oui; OpenMP ne met pas en parallèle les boucles avec la !=condition.

user541686
la source
13

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.

LSchueler
la source
1
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.

RyanP
la source
12

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

à gauche
la source
10

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

// highly contrived example
size_t count_chars(char c, const char* str, size_t len) {
    size_t count = 0;
    bool quoted = false;
    const char* p = str;
    while (p != str + len) {
        if (*p == '"') {
            quote = !quote;
            ++p;
        }
        if (*(p++) == c && !quoted)
            ++count;
    }
    return count;
}

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

if (lhs < rhs) // T.operator <
    lessthan
else if (rhs < lhs) // T.operator < again
    greaterthan
else
    equal

Si lhset rhssont une classe définie par l'utilisateur écrivant ce code comme

if (lhs < rhs) // requires T.operator<
    lessthan
else if (lhs > rhs) // requires T.operator>
    greaterthan
else
    equal

L'implémenteur doit fournir deux fonctions de comparaison. Ainsi <est devenu l'opérateur privilégié.

kfsone
la source
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.

J'espère que cela vous a aidé!

Programmeur aléatoire 123
la source
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.

Adrian McCarthy
la source
3
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.

Ian Newson
la source
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.


la source
1
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.

Fourmi
la source
2

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
  • <ou >est un seul caractère, mais !=deux
MiCo
la source
4
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).

Andre Kostur
la source
1
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.

Cosmin Gherghel
la source
2
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)
Markus