elementen verwijderen uit vector

Status
Niet open voor verdere reacties.

anmarry

Gebruiker
Lid geworden
7 nov 2009
Berichten
6
Hallo,

ik ben nieuw in C++ dus waarschijnlijk is er wel een eenvoudige oplossing voor mijn probleem die ik over het hoofd zie.

Ik heb een vector met objecten (buildings, maar dit is niet van belang) en wil hier willekeurige elementen uit verwijderen op basis van een kans. Ik itereer over de vector en genereer voor elk element een random getal en kijk of dit kleiner is dan een vast getal tussen 0 en 1 (de kans). Als de test slaagt moet het element verwijderd worden.

Momenteel probeer ik een element te verwijderen met erase-remove:

float kans=0.4;
vector<Building>::iterator buildingIt;

for(buildingIt=vector.begin(); buildingIt != vector.end(); ++buildingIt){
Building smallBuilding = *buildingIt;
float getal = (float)rand()/RAND_MAX;
if(getal<kans){ vector.erase(remove(vector.begin(),vector.end(),???),vector.end());
}
}
}


Maar ik weet niet wat ik bij ??? moet gebruiken. Mijn voorwaarde om elementen te verwijderen heeft niets met de elementen zelf te maken. Ik heb ook al aan remove_if gedacht, maar ook hier slaagt het predicaat op de waardes in de vector zelf, en dit heb ik niet nodig. Hoe kan ik dus het best de objecten uit de vector verwijderen? Zijn er eenvoudigere manieren? Moet ik misschien een ander soort container gebruiken dan vector?

bedankt!
 
Laatst bewerkt:
Gebruik voor de variabele niet de naam vector.
In onderstaande code is het "vec".

[cpp]
float kans=0.4;
vector<Building>::iterator buildingIt;
vector<Building> vec;

for(buildingIt=vec.begin(); buildingIt != vec.end(); ++buildingIt){
Building smallBuilding = *buildingIt;
float getal = (float)rand()/RAND_MAX;
if(getal<kans)
vec.erase(buildingIt);
}[/cpp]
 
Bedankt voor de reactie. Ik zie dat vector inderdaad een voorbehouden term is en ik deze niet kan gebruiken in mijn code. Ik wou mijn code een beetje leesbaarder maken om op het forum te posten en had hier niet aan gedacht. Maar dit is eigenlijk niet mijn probleem.

Op deze C++ site lees ik dat de methode erase() iterators die wijzen naar elementen na het verwijderde element ongeldig maakt. In mijn code sluipt hierdoor inderdaad een fout: Na een verwijdering van een element verwijst de iterator naar de nieuwe positie van het element dat na het verwijderde komt. Als een element verwijderd wordt schuiven alle elementen die erna komen op naar links, dus de iterator verwijst nog steeds naar hetzelfde geheugenplaatsje waar het verwijderde element stond (want het element dat erachter kwam staat nu op dit geheugenplaatsje) Omdat ik in een for-lus zit gaat mijn iterator na de erase operatie echter naar het volgende geheugenplaatsje en hierdoor sla ik dus telkens een element over wanneer ik mijn forlus doorloop.

Ik dacht dat de oplossing hiervoor was het gebruiken van erase remove, zoals op wikipedia staat. Maar hier zit ik dus met het probleem dat je alleen elementen kan verwijderen die gelijk zijn aan een bepaalde waarde. Of als je remove_if gebruikt, elementen waarvoor het predicaat waar is. Maar de voorwaarde die ik stel om een element te verwijderen heeft eigenlijk niets te maken met de elementen zelf.

Misschien zitten er fouten in mijn redenering hierboven, of maak ik het moeilijker dan het is. Maar ik zie dat de code zoals ik ze nu heb niet werkt en ik heb het probleem kunnen terugbrengen tot het lijntje code dat in mijn vorige post in het vet staat. Als iemand begrijpt wat ik wil bereiken met dit stukje code en weet hoe dit wel moet zou ik erg dankbaar zijn. Ik heb amper c++ ervaring dus vergeef mij als mijn vraag onduidelijk of erg voor de hand liggend is ;)
 
Misschien begrijp ik je verkeerd maar kan je dan niet zoiets doen als
Code:
if(getal<kans)
{
   vec.erase(buildingIt);
    --buildingIt;
}
Dus de iterator met 1 verminderen?
 
Ondertussen heb ik mijn probleem opgelost. Je kan denk ik geen vector itereren en er tegelijkertijd dingen uit verwijderen of aan toevoegen. Ik heb het opgelost door mijn elementen te kopieren naar een tijdelijke vector en ze over te zetten naar een andere vector als aan de voorwaarde voldaan is. Deze tweede vector geeft mijn functie dan terug.
 
Het is wel mogelijk elementen te verwijderen uit een vector tijdens het itereren. De methode "erase" geeft namelijk de iterator naar het eerstvolgende element terug.

Daar kun je op deze manier gebruik van maken:
[CPP]
float kans = 0.4;
vector<Building>::iterator buildingIt = vec.begin();
vector<Building> vec;

while (buildingIt != vec.end())
{
float getal = (float)rand()/RAND_MAX;

if(getal < kans)
buildingIt = vec.erase(buildingIt);
else
++buildingIt;
}
[/CPP]
 
Ik snap wat je bedoelt en het lijkt op zich wel een goed idee, maar als ik het stukje code zo invoeg compileert alles ok, maar at runtime krijg ik een segmentation fault error. Dit heeft dacht ik iets te maken met ongeldige pointers. Het probleem van elementen die overgeslagen worden is dus inderdaad opgelost, maar de iterator is denk ik wel nog steeds ongeldig geworden door het gebruik van erase voor een element dat niet op het einde van de vector staat.
 
Je zult het nog wel aan moeten passen op je eigen vector. Ik zie nu in mijn voorbeeld dat regel 2 en 3 verkeerd om staan, wat niet gaat werken. :o

Kun je je code eens plaatsen zoals je die nu hebt?

Bij voorkeur binnen een [cpp ] en [/cpp] blok. ;)
 
[cpp]
void constructRealBuilding(){
float kans=0.5;

vector<Building>::iterator buildingIt;

for(buildingIt=vec.begin(); buildingIt != vec.end(); ++buildingIt){
Building smallBuilding = *buildingIt;
float destruct = (float)rand()/RAND_MAX;
// verwijdering gebeurt op basis van kans en een voorwaarde voor de coordinaten van het gebouw
// ter info: p is een vector, de coordinaat van het gebouw, met een x, y en z component
if(destruct<kans && (fabs(smallBuilding.p.x-bigP.x)==(divideCubeSideBy/2)*(smallS*2) ||fabs(smallBuilding.p.y-bigP.y)==(divideCubeSideBy/2)*(smallS*2) || fabs(smallBuilding.p.z-bigP.z)==(divideCubeSideBy/2)*(smallS*2)) ){
buildingIt=vec.erase(buildingIt);
}
else {
++buildingIt;
}
}

}
[/cpp]

Ok, zoals jij de code hebt geschreven werkt ze inderdaad. Maar wanneer ik mijn extra voorwaarden invoeg (deze uitleggen zou een hele schets van mijn programma geven en dat is mss iets te veel detail) krijg ik de segmentation fault. Het ligt nochtans toch niet aan deze voorwaarden, maar aan het gebruik van erase zou ik denken. Want mijn code werkt wel met het gebruik van een tijdelijke vector (zie hieronder)

[cpp]
void constructRealBuilding(){
float kans=0.5;
vector<Building>::iterator buildingIt;
vector<Building> tempVec = vec;
vec.clear();

for(buildingIt=tempVec.begin(); buildingIt != tempVec.end(); ++buildingIt){
Building smallBuilding = *buildingIt;
float destruct = (float)rand()/RAND_MAX;
if(destruct<kans && (fabs(smallBuilding.p.x-bigP.x)==(divideCubeSideBy/2)*(smallS*2) ||fabs(smallBuilding.p.y-bigP.y)==(divideCubeSideBy/2)*(smallS*2) || fabs(smallBuilding.p.z-bigP.z)==(divideCubeSideBy/2)*(smallS*2))){

}
else {
vec.push_back(*buildingIt);
}
}

}
[/cpp]
 
Wat je doet in je eerste code blok is de iterator 2x ophogen, eerst aan het einde van elke loop (regel 6):
[cpp]for(buildingIt=vec.begin(); buildingIt != vec.end(); ++buildingIt){[/cpp]
en nog 1 keer binnen de loop zelf (regel 15):
[cpp] ++buildingIt;[/cpp]
Daardoor zal de iterator te snel oplopen.

De truc van "erase" is dat indien je erase gebruikt binnen de loop, dan keer je de iterator die erase teruggeeft toe aan je eigen iterator (buildingIt). En anders hoog je de iterater 1 op. Dus het ophogen in de for-loop regel kun je daardoor achterwege laten:

[cpp]for(buildingIt=vec.begin(); buildingIt != vec.end(); ){[/cpp]
Dan zou het beter moeten gaan. :)
 
Status
Niet open voor verdere reacties.
Terug
Bovenaan Onderaan