J'ai besoin de revenir en arrière dans un tableau, j'ai donc un code comme celui-ci:
for (int i = myArray.Length - 1; i >= 0; i--)
{
// Do something
myArray[i] = 42;
}
Existe-t-il une meilleure façon de le faire?
Mise à jour: J'espérais que C # avait peut-être un mécanisme intégré pour cela, comme:
foreachbackwards (int i in myArray)
{
// so easy
}
Mise à jour 2: Voici sont meilleurs moyens. Rune prend le prix avec:
for (int i = myArray.Length; i-- > 0; )
{
//do something
}
//or
for (int i = myArray.Length; i --> 0; )
{
// do something
}
qui est encore plus beau en C normal (grâce à Twotymz):
for (int i = lengthOfArray; i--; )
{
//do something
}
Certes, un peu obscur, je dirais que la manière la plus agréable de le faire est de
for (int i = myArray.Length; i --> 0; )
{
//do something
}
En C++, vous avez le choix entre itérer à l'aide d'itérateurs ou d'index. Selon que vous avez un tableau simple ou un std::vector
, tu utilises différentes techniques.
C++ vous permet de faire cela en utilisant std::reverse_iterator:
for(std::vector<T>::reverse_iterator it = v.rbegin(); it != v.rend(); ++it) {
/* std::cout << *it; ... */
}
Le type intégral non signé retourné par std::vector<T>::size
est pas toujours std::size_t
. Cela peut être plus ou moins. Ceci est crucial pour que la boucle fonctionne.
for(std::vector<int>::size_type i = someVector.size() - 1;
i != (std::vector<int>::size_type) -1; i--) {
/* std::cout << someVector[i]; ... */
}
Cela fonctionne, car les valeurs des types intégraux non signés sont définies au moyen de modulo leur nombre de bits. Ainsi, si vous définissez -N
, vous vous retrouvez à (2 ^ BIT_SIZE) -N
Nous utilisons std::reverse_iterator
faire l'itération.
for(std::reverse_iterator<element_type*> it(a + sizeof a / sizeof *a), itb(a);
it != itb;
++it) {
/* std::cout << *it; .... */
}
Nous pouvons utiliser en toute sécurité std::size_t
ici, contrairement à ce qui précède, puisque sizeof
renvoie toujours std::size_t
par définition.
for(std::size_t i = (sizeof a / sizeof *a) - 1; i != (std::size_t) -1; i--) {
/* std::cout << a[i]; ... */
}
En fait, la méthode ci-dessus pour déterminer la taille d'un tableau est nulle. Si a est en fait un pointeur au lieu d'un tableau (ce qui arrive assez souvent et que les débutants le brouilleront), il échouera silencieusement. Un meilleur moyen consiste à utiliser ce qui suit, ce qui échouera au moment de la compilation si un pointeur lui est attribué:
template<typename T, std::size_t N> char (& array_size(T(&)[N]) )[N];
Cela fonctionne en obtenant d'abord la taille du tableau passé, puis en déclarant de renvoyer une référence à un tableau de type char de la même taille. char
est défini pour avoir sizeof
sur: 1. Le tableau retourné aura donc un sizeof
sur: N * 1, ce que nous recherchons, avec seulement compiler évaluation du temps et zéro temps d’exécution.
Au lieu de faire
(sizeof a / sizeof *a)
Changez votre code pour qu'il le fasse maintenant
(sizeof array_size(a))
En C #, utilisation de Visual Studio 2005 ou version ultérieure, tapez 'forr' et appuyez sur [TAB] [TAB]. Cela se développera en une boucle for
qui recule dans une collection.
Il est si facile de se tromper (du moins pour moi), que je pensais que l'insertion de cet extrait serait une bonne idée.
Cela dit, j'aime bien Array.Reverse()
/Enumerable.Reverse()
et puis itérer , puis mieux - ils indiquent plus clairement l'intention.
Je voudrais toujours préférer le code clair contre le code ' plaisant typographiquement '. Ainsi, j'utiliserais toujours:
for (int i = myArray.Length - 1; i >= 0; i--)
{
// Do something ...
}
Vous pouvez le considérer comme le moyen standard de boucler en arrière.
Juste mes deux cents...
Dans C # en utilisant Linq:
foreach(var item in myArray.Reverse())
{
// do something
}
C'est certainement le meilleur moyen pour tout tableau dont la longueur est un type intégral signé. Pour les tableaux dont les longueurs sont de type entier non signé (par exemple, un std::vector
en C++), vous devez alors modifier légèrement la condition de fin:
for(size_t i = myArray.size() - 1; i != (size_t)-1; i--)
// blah
Si vous venez de dire i >= 0
, ceci est toujours vrai pour un entier non signé, la boucle sera donc une boucle infinie.
Cela me semble correct. Si l'indexeur n'était pas signé (uint, etc.), vous devrez peut-être en tenir compte. Appelez-moi paresseux, mais dans ce cas (non signé), je pourrais simplement utiliser une variable de compteur:
uint pos = arr.Length;
for(uint i = 0; i < arr.Length ; i++)
{
arr[--pos] = 42;
}
(En fait, même ici, vous devez faire attention aux cas comme arr.Length = uint.MaxValue ... peut-être un! = quelque part ... bien sûr, c'est un cas très improbable!)
En C j'aime faire ça:
int i = myArray.Length;
while (i--) {
myArray[i] = 42;
}
Exemple C # ajouté par MusiGenesis:
{int i = myArray.Length; while (i-- > 0)
{
myArray[i] = 42;
}}
La meilleure façon de faire cela en C++ est probablement d'utiliser des adaptateurs d'itérateur (ou mieux, de plage), qui transformeront paresseux la séquence au fur et à mesure de son défilement.
Fondamentalement,
vector<value_type> range;
foreach(value_type v, range | reversed)
cout << v;
Affiche la plage "plage" (ici, c'est vide, mais je suis assez sûr que vous pouvez ajouter des éléments vous-même) dans l'ordre inverse. Bien sûr, il n’est pas utile d’itérer simplement la plage, mais il est plutôt cool de transmettre cette nouvelle plage à des algorithmes et à d’autres choses.
Ce mécanisme peut également être utilisé pour des utilisations beaucoup plus puissantes:
range | transformed(f) | filtered(p) | reversed
Calculera paresseusement la plage "plage", où la fonction "f" est appliquée à tous les éléments, les éléments pour lesquels "p" n'est pas vrai sont supprimés et la plage résultante est finalement inversée.
La syntaxe des tuyaux est l’OMI la plus lisible, étant donné qu’elle est infixée. La mise à jour en attente de la bibliothèque Boost.Range l'implémente, mais il est assez simple de le faire vous-même. C'est encore plus cool avec un DSEL lambda de générer la fonction f et le prédicat p en ligne.
Je préfère une boucle while. C'est plus clair pour moi que de décrémenter i
dans la condition d'une boucle for
int i = arrayLength;
while(i)
{
i--;
//do something with array[i]
}
// this is how I always do it
for (i = n; --i >= 0;){
...
}
J'utiliserais le code dans la question initiale, mais si vous vouliez vraiment utiliser foreach et avoir un index entier en C #:
foreach (int i in Enumerable.Range(0, myArray.Length).Reverse())
{
myArray[i] = 42;
}
Je ne suis pas sûr de comprendre pourquoi l’une ou l’autre des solutions de rechange est meilleure, si la bonté inclut la clarté ou la maintenabilité.