I'm trying to remove duplicate combinations of integer vectors stored in a list using a hash table. Iterating over each integer vector in the list, I:
Calculate the hash_value (thash)
See if the hash value is already in the hash table (pids)
If it's in the hash table, erase that vector from the list.
Otherwise, add that value to the hash_table and increment the list
iterator
Print statements seem to confirm my logic, but the loop hangs at the fourth step of iteration. I've commented the it++ and vz.remove(it) that cause the problem and only show the logic in the code below. The code is also available through ideone: https://ideone.com/JLGA0f
#include<iostream>
#include<vector>
#include<list>
#include<cmath>
#include<unordered_set>
using namespace std;
double hash_cz(std::vector<int> &cz, std::vector<double> &lprimes) {
double pid = 0;
for(auto it = cz.begin(); it != cz.end(); it++) {
pid += lprimes[*it];
}
return pid;
}
int main(){
// create list of vectors
std::list<std::vector<int>> vz;
vz.push_back({2,1});
vz.push_back({1,2});
vz.push_back({1,3});
vz.push_back({1,2,3});
vz.push_back({2, 1});
// vector of log of prime numbers
std::vector<double> lprimes {2, 3, 5, 7};
for (auto it = lprimes.begin(); it != lprimes.end(); it++) {
*it = std::log(*it);
}
std::unordered_set<double> pids;
double thash;
for (auto it = vz.begin(); it != vz.end(); ) {
thash = hash_cz(*it, lprimes);
std::cout << thash << std::endl;
// delete element if its already been seen
if (pids.find(thash) != pids.end()) {
std::cout << "already present. should remove from list" << std::endl;
// vz.erase(it);
}
else {
// otherwise add it to hash_table and increment pointer
std::cout << "not present. add to hash. keep in list." << std::endl;
pids.insert(thash);
// it++;
}
it++;
}
for (auto it = vz.begin(); it != vz.end(); it++) {
for (auto j = it -> begin(); j != it -> end(); j++) {
std::cout << *j << ' ';
}
std::cout << std::endl;
}
return 0;
}
Problem is this line of code:
vz.erase(it);
It keeps iterator where it was ie leaves it invalid. It should be either:
vz.erase(it++);
or
it = vz.erase( it );
Note: std::unoredered_set::insert() return value tells you if insert was succesfull or not (if the same value element is there already), you should call it and check result. In your code you do lookup twice:
if (pids.insert(thash).second ) {
// new element added
++it;
} else {
// insertion failed, remove
it = vz.erase( it );
}
As std::list provides remove_if() your code can be simplified:
vz.remove_if( [&pids,&lprimes]( auto &v ) {
return !pids.insert( hash_cz(v, lprimes) ).second );
} );
instead of whole loop.
If the element has already been seen, you erase() the it node and then increment it at the end of the loop: undefined behaviour. Try erase(it++) instead.
If the element has not been seen, you increment it and then do it again at the end of for, yielding UB if it was end() - 1 as it moves past end.
Related
everyone.
I have a question about using the while loop instead of for iterator for my problem.
#include <iostream>
#include <list>
#include <iterator>
using namespace std;
class Okreni
{
public:
list<int> operator () (list <int> g) const {
list<int> :: iterator it1 = g.begin();
list<int> :: iterator it2 = --g.end();
list<int> :: iterator sredina = g.begin();
advance(sredina, (distance(it1,it2))/2);
for(it1; it1 != sredina; it1++){
swap(*it1, *it2);
it2--;
}
if(distance(it1,it2)%2!=0){
swap(*it1,*it2);
}
return g;
}
};
void ispis(list <int> g){
list <int> :: iterator it;
for(it = g.begin(); it != g.end(); it++)
cout << *it << " " ;
cout << endl;
}
list<int> zamijeni(list <int> g){
list<int> :: iterator it1 = g.begin();
list<int> :: iterator it2 = --g.end();
list<int> :: iterator sredina = g.begin();
advance(sredina, (distance(it1,it2))/2);
for(it1; it1 != sredina; it1++){
swap(*it1, *it2);
it2--;
}
if(distance(it1,it2)%2!=0){
swap(*it1,*it2);
}
return g;
}
int main()
{
list <int> lista1;
for(int i = 0; i <= 10; i++){
lista1.push_back(i);
}
cout << "Lista u originalnom redoslijedu" << endl; //List in original order
ispis(lista1);
cout << endl;
list<int> lista2 = zamijeni(lista1);
cout << "Lista u obrnutom redoslijedu" << endl; //This one is List in the reverse order
ispis(lista2);
cout << endl;
cout << "Lista ponovno okrenuta pomocu funkcijskog objekta" << endl; //This one is reversing the list with a function object
Okreni okreni;
list<int> lista3 = okreni(lista2);
ispis(lista3);
return 0;
}
So this is how far I got. I have to create an STL list of integers, using two iterators (one moving forward, the other backward (the sheet defines a bidirectional iterator!)) and a while loop. I have to rotate the order of the elements in the list.
(Also makingsure the solution works for an even and odd number of list elements, and i'm allowed to use the swap algorithm to replace the elements). I have to print the list before and after.
At the end I need to create a function object implemented using a class that will revert the list to its original order, and then print the list to the screen.
As you can see I managed to do the most of my task, but I have trouble with using the while loop instead of for. So I was wondering if you guys could give me some pointers on what should I do with the while loop and if I did something wrong in my task. Thank you all!
well, think about how the for differs from the while loop:
for (int i=0; i < 100; ++i)
vs
while (booleanCondition == true)
the for loop will do three (non-mandatory) operations:
declare a integer variable 'i', that will have the scope of the for
itself
testing the exit condition
define an operation to be run after the cycle ends
The three operations a in a for loop are defined inside the for loop, so that we can rewrite it like: for (A;B;C)
Why non mandatory? Because it is valid to write an empty (and endless) for loop like this:
for(;;) that at that point can only be interrupted by an explicit break inside the code:
int k = 0;
for(;;) {
k = makeKEqualSevenAtSomePoint();
if (k == 7)
break;
}
Please note: this is usually NOT considered a good approach, as defining an infinite loop, usually is not advisable.
If we take into consideration the while loop instead, we find that we only have 1 operation to be defined, and that is the exit condition from the loop:
while(condition) which is the condition B in the for loop (1 < 100 in the example)
So if you need to transform a for into a while, you need to specify the two points A and C:
int i = 0; // declaration A
while (i < 100) { // condition B
++i; // post cycle operation C, implemented in the cycle for the while
}
std::cout << i << std::endl;
For your example, without going into the details of the implementation, the conversion between for and while loop would be from this:
for(it1; it1 != sredina; ++it1){ // this can be rewritten as:
// for(; it1 != sredina; ++it1), as you
// already declared it1, outside of the for loop
swap(*it1, *it2);
it2--;
}
to this:
while(it1 != sredina){
swap(*it1, *it2);
--it2;
++it1;
}
Please note, although you can assume your compiler will optimize it for you, usually you want to use preincrement and not postincrement, as postincrement is less efficient than preincrement (more here: https://medium.com/better-programming/stop-using-i-in-your-loops-1f906520d548 )
This question already has answers here:
Removing item from vector while iterating?
(8 answers)
Closed 2 years ago.
I'm writing this program why it throws an error in toupper('a')?
void test2(void) {
string n;
vector<string> v;
auto it = v.begin();
do {
cout << "Enter a name of a fruit: ";
cin >> n;
v.push_back(n);
} while (n != "Quit");
v.erase(v.end() - 1);
sort(v.begin(), v.end(), [](string g, string l) { return g < l; });
dis(v);
for (auto i : v) {
if (i.at(0) == toupper('a')) {
cout << i << endl;
v.erase(remove(v.begin(), v.end(), i));
}
}
dis(v);
}
Can someone help me to find the error?
Since you already tried to implement the erase-remove-idiom, that's how it can be used in this case:
v.erase(std::remove_if(v.begin(), v.end(), [](const std::string &item) {
return std::toupper(item.at(0)) == 'A';
}), v.end());
Here I assumed, that i.at(0) == toupper('a') is a typo and should be toupper(i.at(0)) == 'A'.
Write your deletion loop like this:
for ( auto it = std::begin( v ); it != std::end( v ); )
{
if ( toupper( it->at( 0 ) ) == 'A' )
it = v.erase( it );
else
++it;
}
If you do it the way you're doing it you'll invalidate the iterator and then never reassign it a valid iterator which is needed to correctly loop through the vector.
The problem here :
for (auto i : v) {
if (i.at(0) == toupper('a')) {
cout << i << endl;
v.erase(remove(v.begin(), v.end(), i));
}
}
is that you are modifying the vector inside the loop with erase() which invalidates the iterators used internally to implement the for range loop.
The loop is a syntactic sugar for something like this :
{
auto&& range = v;
auto&& first = std::begin(v); // obtained once before entering the loop
auto&& last = std::end(v); // obtained once before entering the loop
for (; first != last; ++first)
{
auto i = *first; // first will be invalid the next time after you call erase()
if (i.at(0) == toupper('a')) {
cout << i << endl;
v.erase(remove(v.begin(), v.end(), i)); // you are invalidating the iterators and then dereferencing `first` iterator at the beginning of the next cycle of the loop
}
}
}
Why calling erase() invalidates the vector ?
This is because a vector is like a dynamic array which stores its capacity (whole array size) and size (current elements count), and iterators are like pointers which point to elements in this array
So when erase() is called it will rearrange the array and decrease its size, so updating the end iterator and your first iterator will not be pointing to the next item in the array as you intended . This illustrates the problem :
std::string* arr = new std::string[4];
std::string* first = arr;
std::string* last = arr + 3;
void erase(std::string* it)
{
std::destroy_at(it);
}
for (; first != last; ++first)
{
if (some_condition)
erase(first); // the last element in the array now is invalid
// thus the array length is now considered 3 not 4
// and the last iterator should now be arr + 2
// so you will be dereferencing a destoryed element since you didn't update your last iterator
}
What to learn from this ?
Never do something which invalidates the iterators inside for range loop.
Solution:
Update iterators at each cycle so you always have the correct bounds :
auto&& range = v;
auto&& first = std::begin(v); // obtained once before entering the loop
auto&& last = std::end(v); // obtained once before entering the loop
for (; first != last;)
{
auto i = *first;
if (i.at(0) == toupper('a'))
{
first = v.erase(remove(v.begin(), v.end(), i));
last = std::end(v);
}
else
{
++first;
}
}
I have to process element from a deque (first to last), but at each iteraton I need to use one element and the next too. So I'm trying to write a for loop that starts with mydeque.begin() and finishes at mydeque[mydeque.size()-1]. Also; I would like to know if my iterator deque::iterator it has a next method (it->next()), to make operations like *it - *it->next(). Thank you very much.
Here's how:
#include <deque>
#include <iostream>
int main() {
std::deque<int> test = {1,2,3,4,5,6,7,8};
for(auto i = test.begin(); i != test.end(); i++) {
auto next = std::next(i);
std::cout << *i << *next << std::endl;
if(next==test.end())
{
//Do something. This is the last element.
break;
}
}
}
EDIT: Watch out for deques with only one element. Do this by performing a check such as if(test.begin()==test.end()).
EDIT: My initial solution was indeed a bit error prone and unprecise.
This can easily be done using iterators, see this little example:
#include <deque>
#include <iostream>
int main() {
std::deque<int> test = {1,2,3,4,5,6,7};
for(auto i = test.begin(); i != test.end(); ++i) {
auto next = std::next(i);
if(next != test.end()) {
std::cout << *i << " " << *next << std::endl;
}
}
}
You can simply increment the iterator by 2 and use std::next to also pick the following item (since the offset of std::next defaults to 1).
Here i am trying to print the frequency of each word in the sentence, which is stored in the vector of string
void display_by_word (vector<string> vs) //pass by value is necessary because we need to delete the elements.
{
vector<string> :: size_type vec_size, i;
string to_cmp = vs[0];
int occ = 0;
for ( i = 0; i < vs.size(); ++i){
vector <string> :: iterator it = vs.begin() + 1;
occ = 1;
for ( it ; it != vs.end(); ++it){
if ( vs[i] == *it){
vs.erase(it);
occ++;
}
}
cout << vs[i] << " " << occ << endl;
}
}
Sometimes it works fine but sometimes it crashes.what is wrong?
See http://en.cppreference.com/w/cpp/container/vector/erase
Invalidates iterators and references at or after the point of the erase [...]
After the erase has happened, you cannot reuse it because it has been invalidated. It's undefined behaviour, which can include random crashes.
However, erase returns an iterator to the element following the erased one, or to end() if it was the last element, which is why the solution with it = vs.erase(it); works.
Alternatively, consider using std::remove_if, followed by the two-argument erase, which is known as the Erase-Remove Idiom. It may turn out to be more elegant and more readable than a hand-written loop. Or just rewrite the whole function to use std::count_if.
You may want to rewrite the loop as something like this
while(it != vs.end())
{
if ( vs[i] == *it){
it = vs.erase(it);
occ++;
}
else
it++;
}
You may should do as below:
`
for ( it ; it != vs.end(); ){
if ( vs[i] == *it){
it = vs.erase(it);
occ++;
}
else{
++it;
}
}
`
Apologies if this is obvious, I'm new to C++. There seem to be related answers on stackoverflow, just not those I understand enough to apply in my case.
I have a list of class instances that represent visual patches.
When the distance between features is below a threshold I would like to merge those items, replacing parents with the merged output.
Something like this:
Loop through all items using a nested for loop (to compare each item to every other item)
When a match is found (that is not the same instance):
Construct a new (child) instance from the matching pair, append to new list.
erase both (parent) items from the list
Continue iterating through the list finding other matches
Append the new list to the original list.
I know how to erase items from the list in a single for loop using iterators, but its unclear to me how it would work in a nested loop due to erase() incrementing to the next item.
I may also need to make this function recursive as eventually the merging should reduce the list to a set of representative instances by merging merges.
Suggestions would be appreciated.
Following is my attempt, which does not work (the nested loops interfere with one and other). What is the proper way to do this kind of pairwise comparison of elements in a list?
#include <iostream>
#include <list>
using namespace std;
int main() {
list<int> mylist;
list<int>::iterator mylistiterOutter;
list<int>::iterator mylistiterInner;
for(int i=0; i<10; i++) {
mylist.push_back(i);
cout << i << endl;
}
for(int i=0; i<10; i++) {
mylist.push_back(i);
cout << i << endl;
}
int counter =0;
for(mylistiterOutter = mylist.begin(); mylistiterOutter != mylist.end();) {
cout << "Size of mylist: " << mylist.size() << endl;
for(mylistiterInner = mylist.begin(); mylistiterInner != mylist.end();) {
cout << "mylistiterInner: " << *mylistiterInner << endl;
cout << "mylistiterOutter: " << *mylistiterOutter << endl;
//if (mylistiterOutter == mylistiterInner) {// match!
if (false) {
//mylistiterOutter = mylist.erase(mylistiterOutter);
//mylistiterInner = mylist.erase(mylistiterInner);
} else {
mylistiterOutter++;
mylistiterInner++;
}
counter++;
}
}
cout << endl << "Size of mylist: " << mylist.size() << endl << "NumIterations: " << counter << endl;
return(0);
}
Thanks #lalitm. I tried your approach first because it is closer to what I had originally envisioned, but J.N.'s proposal is more elegant so I'll try that also. Unfortunately I was unable to make #lalitm's approach work. (leads to segmentation fault). Following is slightly more complex code that includes sample class, and merging code, using #lalitm's approach:
#include <iostream>
#include <list>
#include <cmath>
using namespace std;
class percepUnit {
public:
int cx, cy; // location of percept in frame
bool remove; // used to delete percepts
// constructor method
percepUnit(int ix, int iy) {
cx = ix;
cy = iy;
remove = false;
}
};
bool canMerge(percepUnit unitA, percepUnit unitB) {
double dist = sqrt(pow(abs(unitA.cx-unitB.cx),2)+ pow(abs(unitA.cy-unitB.cy),2));
return (dist < 3);
}
percepUnit merge(percepUnit unitA, percepUnit unitB) {
int x,y;
x = unitA.cx+unitB.cx/2;
y = unitA.cy+unitB.cy/2;
return (percepUnit(x,y));
}
int main() {
list<percepUnit> mylist;
list<percepUnit> mergedlist;
list<percepUnit>::iterator mylistiterOutter;
list<percepUnit>::iterator mylistiterInner;
bool mylistiterOutterChanged;
mylist.push_back(percepUnit(0,0));
mylist.push_back(percepUnit(2,2));
mylist.push_back(percepUnit(5,5));
mylist.push_back(percepUnit(7,7));
//cout << "merge front/back? " << canMerge(mylist.front(),mylist.back()) << endl;
//percepUnit test = merge(mylist.front(),mylist.back());
//cout << "merged front/back (x,y): " << test.cx << "," << test.cy << endl;
for(mylistiterOutter = mylist.begin(); mylistiterOutter != mylist.end();) {
cout << "Size of mylist: " << mylist.size() << endl;
mylistiterInner = mylistiterOutter;
mylistiterOutterChanged = false;
for (++mylistiterInner; mylistiterInner != mylist.end();) {
if (canMerge(*mylistiterOutter, *mylistiterInner )) {
mergedlist.push_back(merge(*mylistiterOutter, *mylistiterInner));
mylistiterOutter = mylist.erase(mylistiterOutter);
mylistiterInner = mylist.erase(mylistiterInner);
mylistiterOutterChanged = true;
} else {
++mylistiterInner;
}
}
if (!mylistiterOutterChanged) {
++mylistiterOutter;
}
}
mylist.splice(mylist.end(), mergedlist);
return(0);
}
Here is my gdb info:
Program received signal SIGSEGV, Segmentation fault.
0x00007ffff7b31d97 in std::_List_node_base::unhook() ()
from /usr/lib/libstdc++.so.6
(gdb) bt
#0 0x00007ffff7b31d97 in std::_List_node_base::unhook() ()
from /usr/lib/libstdc++.so.6
#1 0x0000000000401786 in std::list<percepUnit, std::allocator<percepUnit> >::_M_erase (this=0x7fffffffe4d0, __position=...)
at /usr/include/c++/4.4/bits/stl_list.h:1424
#2 0x000000000040153d in std::list<percepUnit, std::allocator<percepUnit> >::erase (this=0x7fffffffe4d0, __position=...)
at /usr/include/c++/4.4/bits/list.tcc:111
#3 0x0000000000401130 in main () at debug.cpp:61
Still no luck. I think the problem could be that the code above does not test if the two iterators are pointing at the same item in the list, and therefore that messes up the iterators (incrementing or not when they should not be).
How can I test if both iterators point at the same item? (without the brute force of comparing all the class members?, but then two copies of the same instance are not the same instance.)
void mergeObjects(std::list<T>& list)
{
std::list<T> new_list;
typedef std::list<T>::iterator Itr;
for (Itr i=list.begin(); i != list.end();)
{
Itr j=i;
bool is_merged = false;
for (++j; j != list.end();)
{
if (isMergeable(*i, *j))
{
T merged = mergeObj(*i, *j);
new_list.push_back(merged);
list.erase(j);
is_merged = true;
break;
}
else
{
++j;
}
}
if (is_merged)
{
i = list.erase(i);
}
else
{
++i;
}
}
list.splice(list.end(), new_list);
}
This should work since inserting and deleting elements does not invalidate any pointers, references and iterator to any other elements. [Ref: The C++ STL Tutorial and Reference, Nikolai Josuttis]
You didn't provide much insight yet, but here is a nice way of doing it:
#include <algorithm>
#include <set>
std::list<int> myList = {5,6,7,7,8,5};
std::set<int> uniques; // a set can only contain unique elements
// copying the list in the set will overwrite the same elements when duplicates are found
std::copy(myList.begin(), myList.end(), std::inserter(uniques, uniques.begin()));
// clear the existing list
myList.clear();
// copy back the unique elements.
std::copy(uniques.begin(), uniques.end(), std::back_inserter(myList));
for (int i: myList)
cout << i << endl;
EDIT : performance wise it should be comparable to your solution or faster because I the searching in the set is done using a 0(log(N)) algorithm, while you have two loops.
EDIT2 : you need something that's a bit more complex than what I thought. One starting piece of advice though: you can't loop with iterators and modify the container you are iterating on at the same time. You'll need to use integer indexes.
EDIT3 : The solution below provides a way using unordered_set. You need to be able to discrimate the objects belonging to the same "groups".
#include <unordered_set>
// That's the objects we are going to "fuse"
// In this example I suppose that all Points that have the same X must be fused
struct Point
{
double x;
double y;
// We need a way to say that which points are equals
bool operator==(const Point& other) const
{
return other.x == x;
}
};
// Then we need a unique identifier to place the points in a set
namespace std {
template <> struct hash<Point> {
double operator()(const Point& point) const {
return point.x;
}
};
}
// We will use an unordered_set to put our elements in
unordered_set<Point> uniques;
// Then we can proceed as before
std::copy(myList.begin(), myList.end(), std::inserter(uniques, uniques.begin()));
myList.clear();
std::copy(uniques.begin(), uniques.end(), std::back_inserter(myList));
I think that modifying (erasing) the list inside nested loop is a bad idea. The outer loop iterator (mylistiterOutter from your example code) will not work properly.
You should make two separate loops. The first one should search for the items to be merged and somehow remember them (without erasing). The second loop would then erase the remembered items and create the new ones.
Following is what I ended up with.
It's ragged: pairs are not compared twice (0,1 and 1,0)
instances are not compared to themselves (0,0)
#include <iostream>
#include <list>
#include <cmath>
#include <algorithm>
using namespace std;
class percepUnit {
public:
int cx, cy; // location of percept in frame
bool remove; // used to delete percepts
// constructor method
percepUnit(int ix, int iy) {
cx = ix;
cy = iy;
remove = false;
}
};
bool canMerge(percepUnit unitA, percepUnit unitB) {
double dist = sqrt(pow(abs(unitA.cx-unitB.cx),2)+ pow(abs(unitA.cy-unitB.cy),2));
return (dist < 3);
}
percepUnit merge(percepUnit unitA, percepUnit unitB) {
int x,y;
x = unitA.cx+unitB.cx/2;
y = unitA.cy+unitB.cy/2;
return (percepUnit(x,y));
}
// Predicate to use remove_if to delete merge inputs.
bool removePercepsMarkedForRemoval(const percepUnit &unit) {
return unit.remove;
}
int main() {
list<percepUnit> mylist;
list<percepUnit> mergedlist;
list<percepUnit>::iterator mylistiterOutter;
list<percepUnit>::iterator mylistiterInner;
mylist.push_back(percepUnit(0,0));
mylist.push_back(percepUnit(2,2));
mylist.push_back(percepUnit(5,5));
mylist.push_back(percepUnit(7,7));
mylist.push_back(percepUnit(15,15));
for(mylistiterOutter = mylist.begin(); mylistiterOutter != mylist.end(); mylistiterOutter++) {
mylistiterInner = mylistiterOutter; // bypass the same pair twice
while (mylistiterInner != mylist.end()) {
if (canMerge(*mylistiterOutter, *mylistiterInner) and mylistiterOutter != mylistiterInner) { // bypass the same instance
mergedlist.push_back(merge(*mylistiterOutter, *mylistiterInner));
mylistiterOutter->remove = true;
mylistiterInner->remove = true;
}
mylistiterInner++;
}
}
mylist.erase(remove_if(mylist.begin(), mylist.end(), removePercepsMarkedForRemoval), mylist.end());
mylist.splice(mylist.end(), mergedlist);
return(0);
}