web-dev-qa-db-fra.com

Faut-il utiliser <ou <= dans une boucle for

Si vous deviez parcourir une boucle 7 fois, utiliseriez-vous:

for (int i = 0; i < 7; i++)

ou:

for (int i = 0; i <= 6; i++)

Il y a deux considérations:

  • performance
  • lisibilité

Pour les performances, je suppose que Java ou C #. Est-ce important si "inférieur à" ou "inférieur ou égal à" est utilisé? Si vous avez un aperçu d'un autre langage, veuillez indiquer lequel .

Pour plus de lisibilité, je suppose des tableaux basés sur 0.

PD: Ma mention de tableaux basés sur 0 peut avoir confondu les choses. Je ne parle pas d'itérer à travers des éléments de tableau. Juste une boucle générale.

Il y a un bon point ci-dessous sur l'utilisation d'une constante qui expliquerait ce qu'est ce nombre magique. Donc, si j'avais "int NUMBER_OF_THINGS = 7" puis "i <= NUMBER_OF_THINGS - 1 "aurait l'air bizarre, non?.

124
Eugene Katz

Le premier est plus idiomatique . En particulier, il indique (dans un sens basé sur 0) le nombre d'itérations. Lorsque j'utilise quelque chose basé sur 1 (par exemple JDBC, IIRC), je pourrais être tenté d'utiliser <=. Alors:

for (int i=0; i < count; i++) // For 0-based APIs

for (int i=1; i <= count; i++) // For 1-based APIs

Je m'attendrais à ce que la différence de performances soit insignifiante dans le code du monde réel.

286
Jon Skeet

Ces deux boucles itèrent 7 fois. Je dirais que celui avec un 7 est plus lisible/plus clair, sauf si vous avez une très bonne raison pour l'autre.

72
Steve Losh

Je me souviens de mes jours où nous avons fait 8086 Assembly au collège, c'était plus performant de faire:

for (int i = 6; i > -1; i--)

car il y avait une opération JNS qui signifie Jump if No Sign. L'utilisation de cela signifiait qu'il n'y avait pas de recherche de mémoire après chaque cycle pour obtenir la valeur de comparaison et aucune comparaison non plus. De nos jours, la plupart des compilateurs optimisent l'utilisation des registres, de sorte que la mémoire n'est plus importante, mais vous obtenez toujours une comparaison non requise.

Soit dit en passant, mettre 7 ou 6 dans votre boucle introduit un " nombre magique ". Pour une meilleure lisibilité, vous devez utiliser une constante avec un nom révélateur d'intention. Comme ça:

const int NUMBER_OF_CARS = 7;
for (int i = 0; i < NUMBER_OF_CARS; i++)

EDIT: Les gens ne reçoivent pas le truc de l'Assemblée, donc un exemple plus complet est évidemment nécessaire:

Si nous faisons pour (i = 0; i <= 10; i ++), vous devez le faire:

    mov esi, 0
loopStartLabel:
                ; Do some stuff
    inc esi
                ; Note cmp command on next line
    cmp esi, 10
    jle exitLoopLabel
    jmp loopStartLabel
exitLoopLabel:

Si nous le faisons pour (int i = 10; i> -1; i--), alors vous pouvez vous en sortir:

    mov esi, 10
loopStartLabel:
                ; Do some stuff
    dec esi
                ; Note no cmp command on next line
    jns exitLoopLabel
    jmp loopStartLabel
exitLoopLabel:

Je viens de vérifier et le compilateur C++ de Microsoft ne fait pas cette optimisation, mais il le fait si vous le faites:

for (int i = 10; i >= 0; i--) 

Donc, la morale est que si vous utilisez Microsoft C++ †, et que la montée ou la descente ne fait aucune différence, pour obtenir une boucle rapide, vous devez utiliser:

for (int i = 10; i >= 0; i--)

plutôt que l'un ou l'autre:

for (int i = 10; i > -1; i--)
for (int i = 0; i <= 10; i++)

Mais franchement, obtenir la lisibilité de "for (int i = 0; i <= 10; i ++)" est normalement beaucoup plus important que de manquer une commande de processeur.

† D'autres compilateurs peuvent faire des choses différentes.

55
Martin Brown

J'utilise toujours <array.length car il est plus facile à lire que <= array.length-1.

ayant également <7 et étant donné que vous savez que cela commence par un index 0, il devrait être intuitif que le nombre soit le nombre d'itérations.

27
Omar Kooheji

Vu d'un point de vue optimisant, cela n'a pas d'importance.

Vu d'un point de vue de style de code, je préfère <. Raison:

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

est tellement plus lisible que

for ( int i = 0; i <= array.size() -1; i++ )

<vous donne également le nombre d'itérations immédiatement.

Un autre vote pour <est que vous pourriez éviter beaucoup d'erreurs accidentelles ponctuelles.

18
erlando

@Chris, Votre déclaration sur le fait que .Length coûte cher dans .NET est en fait fausse et dans le cas des types simples, c'est exactement le contraire.

int len = somearray.Length;
for(i = 0; i < len; i++)
{
  somearray[i].something();
}

est en fait plus lent que

for(i = 0; i < somearray.Length; i++)
{
  somearray[i].something();
}

Le dernier est un cas optimisé par le runtime. Étant donné que le runtime peut garantir que i est un index valide dans le tableau, aucune vérification des limites n'est effectuée. Dans le premier, le runtime ne peut garantir que je n'ai pas été modifié avant la boucle et force les vérifications des limites sur le tableau pour chaque recherche d'index.

10
Jeff Mc

Cela ne fait aucune différence en termes de performances. Par conséquent, j'utiliserais ce qui est plus facile à comprendre dans le contexte du problème que vous résolvez.

6
Phil Wright

Je préfère:

for (int i = 0; i < 7; i++)

Je pense que cela se traduit plus facilement par "itérer 7 fois dans une boucle".

Je ne suis pas sûr des implications en termes de performances - je soupçonne que toute différence serait compilée.

5
Dominic Rodger

Dans Java 1.5 vous pouvez simplement faire

for (int i: myArray) {
    ...
}

donc pour le cas du tableau, vous n'avez pas à vous inquiéter.

4
JesperE

Je ne pense pas qu'il y ait de différence de performance. Le deuxième formulaire est certainement plus lisible, vous n'avez pas à en soustraire mentalement un pour trouver le dernier numéro d'itération.

EDIT: Je vois d'autres en désaccord. Pour moi personnellement, j'aime voir les numéros d'index réels dans la structure de la boucle. C'est peut-être parce que cela rappelle plus la syntaxe 0..6 De Perl, qui je sais est équivalente à (0,1,2,3,4,5,6). Si je vois un 7, je dois vérifier l'opérateur à côté de lui pour voir qu'en fait, l'index 7 n'est jamais atteint.

4
Adam Bellaire

Je dirais utiliser la version "<7" parce que c'est ce que la majorité des gens liront - donc si les gens lisent votre code, ils pourraient l'interpréter à tort.

Je ne m'inquiéterais pas de savoir si "<" est plus rapide que "<=", optez simplement pour la lisibilité.

Si vous voulez opter pour une augmentation de vitesse, tenez compte des points suivants:

for (int i = 0; i < this->GetCount(); i++)
{
  // Do something
}

Pour augmenter les performances, vous pouvez légèrement le réorganiser pour:

const int count = this->GetCount();
for (int i = 0; i < count; ++i)
{
  // Do something
}

Notez la suppression de GetCount () de la boucle (car cela sera interrogé dans chaque boucle) et le changement de "i ++" en "++ i".

4
Mark Ingram

Edsger Dijkstra a écrit un article à ce sujet en 1982 où il plaide pour <<i <upper <:

Il existe un plus petit nombre naturel. L'exclusion de la borne inférieure - comme en b) et d) - force pour une sous-séquence commençant au plus petit nombre naturel la borne inférieure comme mentionné dans le domaine des nombres non naturels. C'est moche, donc pour la borne inférieure, nous préférons le ≤ comme dans a) et c). Considérez maintenant les sous-séquences commençant au plus petit nombre naturel: l'inclusion de la borne supérieure forcerait alors ce dernier à ne pas être naturel au moment où la séquence s'est rétrécie pour devenir vide. C'est moche, donc pour la borne supérieure, nous préférons <comme dans a) et d). Nous concluons que la convention a) doit être préférée.

4
Martijn

En C++, je préfère utiliser !=, utilisable avec tous les conteneurs STL. Tous les itérateurs de conteneurs STL ne sont pas comparables.

4
madmoose

Tout d'abord, n'utilisez pas 6 ou 7.

Mieux utiliser:

int numberOfDays = 7;
for (int day = 0; day < numberOfDays ; day++){

}

Dans ce cas, c'est mieux que d'utiliser

for (int day = 0; day <= numberOfDays  - 1; day++){

}

Encore mieux (Java/C #):

for(int day = 0; day < dayArray.Length; i++){

}

Et encore mieux (C #)

foreach (int day in days){// day : days in Java

}

La boucle inverse est en effet plus rapide mais comme c'est plus difficile à lire (sinon par vous par d'autres programmeurs), il vaut mieux éviter dedans. Surtout en C #, Java ...

3
Carra

Je suis d'accord avec la foule qui dit que le 7 a du sens dans ce cas, mais j'ajouterais que dans le cas où le 6 est important, disons que vous voulez préciser que vous n'agissez que sur des objets jusqu'au 6ème index, puis le <= est meilleur car il rend les 6 plus faciles à voir.

2
tloach

De retour à l'université, je me souviens que ces deux opérations étaient similaires en temps de calcul sur le CPU. Bien sûr, nous parlons au niveau de l'Assemblée.

Cependant, si vous parlez C # ou Java, je ne pense vraiment pas que l'un va augmenter la vitesse par rapport à l'autre.Les quelques nanosecondes que vous gagnez ne valent probablement pas la confusion que vous introduisez.

Personnellement, j'écrirais le code qui a du sens du point de vue de l'implémentation métier et je m'assurerais qu'il est facile à lire.

2
casademora

Cela tombe directement sous la catégorie "Rendre le mauvais code faux" .

Dans les langages d'indexation à base zéro, tels que Java ou C #, les utilisateurs sont habitués aux variations du index < count condition. Ainsi, tirer parti de cette convention de facto rendrait les erreurs de coup par coup plus évidentes.

Concernant les performances: tout bon compilateur digne de son encombrement mémoire doit être rendu tel qu'un non-problème.

2
Ryan Delucchi

En passant, lorsque je fais une boucle dans un tableau ou une autre collection en .Net, je trouve

foreach (string item in myarray)
{
    System.Console.WriteLine(item);
}

pour être plus lisible que la boucle for numérique. Cela suppose bien sûr que le compteur réel Int lui-même n'est pas utilisé dans le code de boucle. Je ne sais pas s'il y a un changement de performance.

2
Rob Allen

Prendre l'habitude d'utiliser <le rendra cohérent pour vous et le lecteur lorsque vous parcourez un tableau. Il sera plus simple pour tout le monde d'avoir une convention standard. Et si vous utilisez un langage avec des tableaux basés sur 0, <est la convention.

Cela importe certainement plus que toute différence de performances entre <et <=. Visez d'abord la fonctionnalité et la lisibilité, puis optimisez.

Une autre remarque est qu'il serait préférable d'avoir l'habitude de faire ++ i plutôt que i ++, car fetch et increment nécessite un temporaire et increment et fetch ne le fait pas. Pour les entiers, votre compilateur optimisera probablement l'écart temporaire, mais si votre type d'itération est plus complexe, il pourrait ne pas être en mesure de le faire.

1
JohnMcG

Vous pouvez également utiliser != au lieu. De cette façon, vous obtiendrez une boucle infinie si vous faites une erreur lors de l'initialisation, ce qui fait que l'erreur est remarquée plus tôt et que tout problème qu'elle provoque se limite à rester coincé dans la boucle (plutôt que d'avoir un problème beaucoup plus tard et de ne pas trouver il).

1
Brian

N'utilisez pas de nombres magiques.

Pourquoi est-il 7? (ou 6 d'ailleurs).

utilisez le bon symbole pour le nombre que vous souhaitez utiliser ...

Dans ce cas, je pense qu'il vaut mieux utiliser

for ( int i = 0; i < array.size(); i++ )
1
Krakkos

Grande question. Ma réponse: utilisez le type A ('<')

  • Vous voyez clairement combien d'itérations vous avez (7).
  • La différence entre deux points d'extrémité est la largeur de la plage
  • Moins de caractères le rend plus lisible
  • Vous avez plus souvent le nombre total d'éléments i < strlen(s) plutôt que l'index du dernier élément donc l'uniformité est importante.

Un autre problème est avec toute cette construction. i apparaît fois dedans, donc il peut être mal tapé. La construction for-loop dit comment faire au lieu de que faire. Je suggère d'adopter ceci:

BOOST_FOREACH(i, IntegerInterval(0,7))

C'est plus clair, compile pour exaclty les mêmes instructions asm, etc. Demandez-moi le code d'IntegerInterval si vous le souhaitez.

1
Pavel Radzivilovsky

Tant de réponses ... mais je crois que j'ai quelque chose à ajouter.

Ma préférence est que les nombres littéraux indiquent clairement quelles valeurs "i" prendra dans la boucle. Donc, dans le cas d'une itération à partir d'un tableau à base zéro:

for (int i = 0; i <= array.Length - 1; ++i)

Et si vous êtes en boucle, pas en itération dans un tableau, compter de 1 à 7 est assez intuitif:

for (int i = 1; i <= 7; ++i)

La lisibilité l'emporte sur les performances jusqu'à ce que vous le profiliez, car vous ne savez probablement pas ce que le compilateur ou l'exécution va faire avec votre code jusque-là.

1
Nick Westgate

Comme les gens l'ont observé, il n'y a pas de différence dans les deux alternatives que vous avez mentionnées. Juste pour confirmer cela, j'ai fait quelques analyses comparatives simples en JavaScript.

Vous pouvez voir les résultats ici . Ce qui n'est pas clair, c'est que si j'échange la position des 1er et 2e tests, les résultats de ces 2 tests s'échangent, c'est clairement un problème de mémoire. Cependant le 3ème test, celui où j'inverse l'ordre de l'itération est nettement plus rapide.

1
David Wees

Il y a plusieurs bonnes raisons d'écrire i <7. Avoir le numéro 7 dans une boucle qui répète 7 fois est bien. La performance est effectivement identique. Presque tout le monde écrit i <7. Si vous écrivez pour plus de lisibilité, utilisez le formulaire que tout le monde reconnaîtra instantanément.

1
DJClayworth

Comme tout le monde le dit, il est habituel d'utiliser des itérateurs indexés 0 même pour des choses en dehors des tableaux. Si tout commence à 0 et se termine à n-1 et les bornes inférieures sont toujours <= et les limites supérieures sont toujours <, il y a beaucoup moins de réflexion à faire lors de l'examen du code.

1
ephemient

Les opérateurs "<" et "<=" ont exactement le même coût de performance.

L'opérateur "<" est un standard et plus facile à lire dans une boucle de base zéro.

L'utilisation de ++ i au lieu d'i ++ améliore les performances en C++, mais pas en C # - je ne connais pas Java.

1
Jeff B

J'ai toujours préféré:

for ( int count = 7 ; count > 0 ; -- count )
1
kenny

Aucune différence de vitesse, mais <est plus susceptible d'être correct dans une langue avec des tableaux basés sur 0. De plus, si vous voulez répéter vers le bas au lieu de monter, vous pouvez dire:

for (i = 7; --i >= 0; ) ...
0
Mike Dunlavey

Il ne devrait pas y avoir de différence de performances au moins avec les compilateurs x86. JL et JLE travaille en même temps, dès que je sais. Et en ce qui concerne la redability, l'utilisation de "<7" pour un tableau de sept éléments est logique.

0
akalenuk

Je dirais que cela devrait être <.

Pourquoi utiliser beaucoup de mots quand quelques-uns suffiront. Un test est plus facile à comprendre que deux. Par conséquent, il est plus facile de tester et de modifier les unités à l'avenir.

La différence est-elle petite? Oui. Mais pourquoi ajouter de la complexité quand cela n'est pas justifié.

Enfin, vous ne dépendez d'aucun optimiseur ou implémentation d'un interpréteur lorsque le code est optimisé pour commencer. Pour citer Einstein, "restez aussi simple que possible mais pas plus simple".

0
Cam Wolff

Je suis la première méthode car cet idiome est répété fréquemment.

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

String s = oldString.substring (0, numChars);

etc.

Je suis habitué à ce que la limite supérieure soit exclue, et je préférerais que cela reste ainsi sauf s'il y a de bonnes raisons de la changer. (exemple - indexation basée sur 1)

0
Chris Cudmore

Optez d'abord pour la lisibilité, optimisez plus tard (même si, honnêtement, je ne peux imaginer aucune différence qui serait perceptible).

Sachez que la forme 0 -> K est un idiome C transmis en C # en ayant des tableaux basés sur 0. Suivez l'idiome et ne violez pas le principe du moindre étonnement.

0
plinth

Je préfère ça:

for (int i = 0; i < 7; i++)

Cependant (ce n'est qu'une idée), la lisibilité de celui-ci pourrait avoir à faire si les tableaux sont basés sur 0 (C #, Java) ou 1 (VB .NET). Je dis cela parce que lorsque vous travaillez avec des tableaux basés sur 0, vous obtenez dans un état d'esprit que 0-6 s'exécuterait 7 fois. Je pense que 0-6 est plus intuitif que 1-7. Là encore, je viens d'un milieu C++, Java, C #.

0
Ryan Rodemoyer

Pour certains langages/technologies comme .NET, utiliser .Size ou .Length ou size ()/length () est une mauvaise idée, car il accède à cette propriété à chaque itération, donc l'affecter à une variable a un impact sur les performances légèrement inférieur.

0
Chris S

Je pense que les deux sont OK, mais quand vous avez choisi, respectez l'un ou l'autre. Si vous avez l'habitude d'utiliser <=, essayez de ne pas utiliser <et vice versa.

Je préfère <=, mais dans les situations où vous travaillez avec des index qui commencent à zéro, j'essaierais probablement d'utiliser <. C'est une préférence personnelle cependant.

0
seanyboy

D'un point de vue logique, il faut penser que < count serait plus efficace que <= count pour la raison exacte que <= testera également l'égalité.

0
Henry B

L'optimisation prématurée est la racine de tout Mal. Optez pour la lisibilité à moins qu'il n'y ait une très bonne raison de vous inquiéter pour < plus de <=.

0
cciotti