OVH Cloud OVH Cloud

boucle for sur std::vector [debutant]

48 réponses
Avatar
AG
Bonjour,

je débute avec l'utiliation des vecteurs de la stl, et je me demandais
comment écrire une boucle for. J'ai vu dans les news une méthode assez
courante :



std::vector<int> n;

for(std::vector<int>::iterator i=n.begin();i!=n.end();i++)
{
}

Je trouve cette méthode lourde à écrire. Plus simplement j'aurais vu
(entre autre) :

std::vector<int> n;

for(int i=0;i<n.size();i++)
{
}


Que choisir ? Si je n'ai pas besoin de l'iterator dans la boucle, la
seconde solution est-elle meilleure (plus lisible, plus simple, plus
rapide ? )

AG.

10 réponses

1 2 3 4 5
Avatar
AG
Olivier Azeau wrote:
Le fait est qu'initialiser 2 vector dans une même boucle est difficile à
justifier excepté pour de l'optimisation (et encore dans ce cas là, ça
se discute)
Mon problème n'est pas l'initialisation. Mon problème est de réaliser

une boucle sur deux vecteurs en même temps. Je veux bien les initialiser
séparément, mais a un moment donné, j'aurais besoin de faire des calculs
avec les deux en même temps.



En pratique, soit tes 2 vector de même taille sont dans une même classe
et alors je ne vois pas de raison pour ne pas en faire un seul,
C'est cette phrase que je ne comprends pas. Pourquoi une classe ne

pourrait elle pas avoir deux vecteurs de même taille ?


soit ils
sont dans des classes différentes et alors je ne vois pas de raison pour
les initialiser simultanément.
L'initialisation, je veux bien, mais passer les valeurs d'un vecteur, à

l'autre, en ajoutant une petite opération entre temps, c'est tout à fait
possible.



Bon allez, je te file mon exemple sur lequel je travaille.

J'ai une classe RandInt qui me permet de générer un vecteur "n"
d'unsigned int tiré "aléatoirement"

Je veux ensuite transformer ce vecteur en vecteur de doubles "d" compris
entre 0 et 1, en divisant mon vecteur "n" par la plus grand valeur
d'unsigned int possible (appelons là RandIntMax)

je suis bien obligé de faire quelque chose du genre :

std::vector<unsigned int> n(20);
std::vector<double> d(20);
unsigned int RandIntMax;


// initialisation de n comme tu veux, peu importe
n=...
//idem pour d
d=...

// initialisation de d ;

std::vector<unsigned int>::iterator i_n;
std::vector<double>::iterator i_d;

i_n = n.begin();
i_d = d.begin();

for(;i_n!=n.end();++i_n,++i_d)
{
*i_d = static_cast<double>((*i_n))/RandIntMax;
}


J'ai pris l'exemple du passage de vector<unsigned int> vers
vector<double> mais j'aurais pu prendre vector<unsigned int> vers
vector<unsigned int> si j'avais voulu transformer mes entiers aléatoires
en bits (0 ou 1) en les comparant à RandIntMax/2.

Est ce que c'est plus clair ? Je pense aussi que ce problème est
indépendant de la notion de classe, non ?

Avatar
Falk Tannhäuser
AG wrote:
std::vector<unsigned int> n(20);
std::vector<double> d(20);
unsigned int RandIntMax;
[...]

std::vector<unsigned int>::iterator i_n;
std::vector<double>::iterator i_d;

i_n = n.begin();
i_d = d.begin();

for(;i_n!=n.end();++i_n,++i_d)
{
*i_d = static_cast<double>((*i_n))/RandIntMax;
}


Et pourquoi pas un

std::transform(n.begin(), n.end(), std::back_inserter(d),
std::bind2nd(std::divides<double>(), RandIntMax));

(en supposant que 'd' soit vide) - éventuellement précédé
d'un 'd.reserve(n.size());' en guise d'optimisation ?

Falk

Avatar
kanze
Fabien LE LEZ wrote:
On 9 Feb 2005 05:29:33 -0800, :

Bof... Je te conseille d'écrire systématiquement ++i, et
de remettre à plus tard l'idée qu'on peut écrire i++.


Pour quelles raisons precisement?


Pour faire de l'optimisation prématurée:-).


Pas forcément. C'est comme passer un std::string par référence
constante au lieu de le passer par valeur : c'est un idiome ;
si on ne le respecte pas, le lecteur va passer du temps à se
demander pourquoi.


Le problème, c'est qu'il y a aussi un idiome qui met les ++
après ; c'est même l'idiome le plus ancien et le plus répandu.

Si on travaille de zéro, sur un nouveau projet, on peut choisir
l'idiome qu'on veut. Dans ce cas (et dans ce cas seulement), le
fait que la forme préfixé ne serait jamais plus lent est un
argument. Pas un argument très fort, mais dans l'absence
d'autres arguments, on n'a pas vraiment besoin d'un argument
très fort.

Si on travaille dans un contexte où l'idiome a déjà été choisi
(parce qu'il y a du code existant ou pour d'autres raisons), il
n'y a pas de raison de le changer.

La première règle ici est donc de faire comme les autres.

(Note que ce n'est pas toujours la première règle. Il y a bien
des cas où un changement apportera assez d'améliorations pour
qu'il vaut le coup. Ceci n'en est pas un.)

--
James Kanze GABI Software
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34




Avatar
kanze
Loïc Joly wrote:
Matthieu Moy wrote:
Fabien LE LEZ writes:

On Wed, 09 Feb 2005 11:11:22 +0100, AG :

Permet moi, en tant que débutant, de repousser ce genre de
détails à plus tard,


Bof... Je te conseille d'écrire systématiquement ++i, et de
remettre à plus tard l'idée qu'on peut écrire i++.


Oui, mais alors, il faut programmer en ++C ?


Bin, C++, ça veut bien dire qu'on prend C, qu'on l'incrémente
quelquepart dans son coin, mais que ce que voit l'utilisateur,
çà reste C... Donc ce serait pas mal effectivement, le ++C...


Il y a plus de vérité là-dedans que tu n'y crois. Une des
raisons de la réussite de C++, c'est qu'il n'obligeait pas à des
vieux utilisateurs à changer leur point de vue. Une raison plus
valable de sa réussite, c'est que dans le code existant, on
avait bien accès à l'ancienne valeur, et que ce n'était que par
la suite, dans de nouveaux codes, que la nouvelle valeur
servait.

--
James Kanze GABI Software
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34




Avatar
AG
Falk Tannhäuser wrote:

AG wrote:

std::vector<unsigned int> n(20);
std::vector<double> d(20);
unsigned int RandIntMax;


[...]

std::vector<unsigned int>::iterator i_n;
std::vector<double>::iterator i_d;

i_n = n.begin();
i_d = d.begin();

for(;i_n!=n.end();++i_n,++i_d)
{
*i_d = static_cast<double>((*i_n))/RandIntMax;
}



Et pourquoi pas un

std::transform(n.begin(), n.end(), std::back_inserter(d),
std::bind2nd(std::divides<double>(), RandIntMax));

(en supposant que 'd' soit vide) - éventuellement précédé
d'un 'd.reserve(n.size());' en guise d'optimisation ?
Oui, c'est visiblement le genre de solution vers laquelle j'ai fini par

me retourner, comme en témoigne mon poste plus bas, avec la fonction
generate. (peut être n'est il pas sur votre serveur, car personne n'y
répond...).

Merci pour ces pointeurs, je vais voir avec.


Avatar
Alexandre

Il te faudrait donc 21 entiers :-)

mais non, personne ne met jamais 20, voyons ;-)


Avatar
Alexandre
std::generate(bits.begin(),bits.end(),BitGen::GetBit);

bitgen.cpp(17) : error C2664: 'generate' : cannot convert parameter 3 from
'int (void)' to 'int (__thiscall *)(void)'
None of the functions with this name in scope match the target
type


à priori c'est parce que tu passes l'adresse d'une fonction membre, or à
priori l'algo generate ne peut pas y accéder (il n'a pas le this qui serait
utile pour, grosso modo...).
1ère solution : faire une fonction hors de toute classe comme prédicat
(bof...)
2e solution : ecrire un foncteur (un objet-fonction) cad une classe
possédant l'operator () (operator() ) prenant, dans ton cas, un int (si ton
vector bits contient un int). Et tu passes une instance de cet objet à
generate.

Avatar
Falk Tannhäuser
AG wrote:

class BitGen : public RandInt
{
public:
int GetBit(void);
void GetBits( std::vector<int> & bits);
};

int BitGen::GetBit(void)
{
return draw_one()<GetRandMax()/2 ? 0 : 1;
}

void BitGen::GetBits(std::vector<int> & bits)
{
std::generate(bits.begin(),bits.end(),BitGen::GetBit);
}


std::generate nécessite un foncteur supportant l'opérateur ()
pouvant être appelé sans arguments - il faut donc que
- soit la fonction membre 'int GetBit()' soit statique ;
la seule façon de lui passer du contexte est alors en
se servant de variables globales ou (statiques membres
de la classe BitGen) - pas terrible...
- soit tu passes un objets d'une classe dans laquelle
tu as surchargé l'opérateur () ; tu peux alors passer
du contexte dans les variables membres de cet objet.
Exemple :

class NumGen
{
int begin;
int end;
public:
NumGen(int b, int e) : begin(b), end(e) {}
int operator()() { return begin + rand() % (end-begin); }
};

// ...
std::vector<int> bits;
std::generate_n(std::back_inserter(bits), 20, NumGen(3, 8));

Falk

Avatar
AG
std::generate nécessite un foncteur supportant l'opérateur ()
pouvant être appelé sans arguments - il faut donc que
- soit la fonction membre 'int GetBit()' soit statique ;
la seule façon de lui passer du contexte est alors en
se servant de variables globales ou (statiques membres
de la classe BitGen) - pas terrible...
- soit tu passes un objets d'une classe dans laquelle
tu as surchargé l'opérateur () ; tu peux alors passer
du contexte dans les variables membres de cet objet.
Exemple :

class NumGen
{
int begin;
int end;
public:
NumGen(int b, int e) : begin(b), end(e) {}
int operator()() { return begin + rand() % (end-begin); }
};

// ...
std::vector<int> bits;
std::generate_n(std::back_inserter(bits), 20, NumGen(3, 8));

Falk
Merci, je crois qu'un grand tour dans mon livre de C++ est plus que

nécessaire maintenant.

AG.

Avatar
Olivier Azeau
korchkidu wrote:
Olivier Azeau wrote:

En pratique, soit tes 2 vector de même taille sont dans une même
classe et alors je ne vois pas de raison pour ne pas en faire un seul,


C'est un peu rapide comme conclusion je trouve.


Probablement... :-)

Pour moi, une animation est un vector d'ensemble d'articulation. Si je
dois gerer 2 animations en paralleles et seulement 2, je ne vois
vraiment pas pourquoi je ne pourrais pas avoir 2 vector de la meme
taille... Parce que faire une structure pour ca, c'est un peu trop je
trouve...


Là je ne comprends pas. Tu dis toi même "une animation est un vector
d'ensemble d'articulation" ce qui laisse supposer une classe "animation"
comme bon début de design, puis "je dois gerer 2 animations en
paralleles", ce qui laisse supposer 2 instances de cette classe.

Ce qui me chiffonne c'est le "et seulement 2".
Cela signifie-t-il que ton argument est l'absence de besoin de
réutilisabilité ? (i.e. pas besoin de faire une classe pour un truc que
je n'utilise que 2 fois)
Si tel est le cas, j'apprécie : la reutilisabilité prématurée n'est pas
un bon critère de design pour moi non plus.
Mais dans le cas présent, mon argument c'est la lisibilité.
Si ta manière d'énoncer un problème clairement met en évidence une
classe, il y a, à mon avis, beaucoup de chances pour que la meilleure
lisibilité du code soit obtenue en écrivant une telle classe (en tt cas
moi je n'hésite pas).


1 2 3 4 5