How can I pass iterators as function arguments? - c++

I'm working on some c++ problems from a book and my task is to count number occurrences in a list. The task can be solved with just one iterator walking on each item of the list. However, I've done this before with other other task and I'm trying something different with this one. The idea is basically to use two iterators that increase a counter whenever the step on a value until they meet each other. I'm done with that for just the first value of the list but now I want to do it for the rest of the different numbers of the list. The main problem currently is that I can't find out why the programs hangs when I pass an iterator as an argument since creating the start iterator within the findNumberOccurrences and initialize it with the input.begin() value doesn't have any problem if that makes sense.
#include <functional>
#include <iostream>
#include <iterator>
#include <list>
int findNumberOccurences(std::list<int> input, std::list<int>::iterator& start) {
std::cout << *start << '\n';
std::list<int>::iterator end = input.end();
int count = 1;
while (std::distance(input.begin(), start) != std::distance(input.begin(), end)) {
while (*end != *start) {
--end;
}
++count;
++start;
if (std::distance(input.begin(), start) == std::distance(input.begin(), end)) {
break;
}
while (*start != *end) {
std::cout << *start << '\n';
++start;
}
++count;
--end;
}
return count;
}
int main() {
std::list<int> input = {3, 4, 4, 2, 3, 3, 4, 3, 2};
std::list<int>::iterator start = input.begin();
int count = findNumberOccurences(input, start);
std::cout << count << '\n';
return 0;
}
I've written some cout logs within each while loop but I don't anything that gives me a clue about what's going on. Weird thing is that my terminal cursor keeps blinking without throwing exception like when having an infinite loop.
Can anyone shed some light on what I'm doing wrong please?
Cheers!

Alright, I've just found the answer to this question. Basically, I must pass a reference of the list as a function argument like this:
int findNumberOfOccurrences(std::list<int> &input, std::list<int>::iterator &start)
I did that intuitively but I want to know why this solves the problem so I'm gonna read about references. If anyone wants add anything to the answer, please by all means.

Passing the container by reference "fixes" the immediate problem in the code, but the code itself is flawed. When you're dealing with iterators you don't need the underlying container; indeed, there often isn't an underlying container. That's the point of iterators: they provide an abstraction that gives your code more flexibility.
In this case, just write the function to take two iterators:
int findNumberOccurences(std::list<int>::iterator first,
std::list<int>::iterator last) {
// code goes here
}
It's not clear to me what the code in the question is actually doing, so I haven't reproduced it. But in general, you can use first and last as the two ends of the data sequence and manipulate them appropriately.
while (first != last) {
// do something with *first
++first;
}

Related

Finding most common element in a list (C++ STL)?

I have a program where I have to find the most common element in a list of integers. I do this with the program below, but the problem is, I suspect that the erase function messes up with the iterator incrementation in the countRepetition() function. My question is how can I fix the problem or if this is not the issue what is it?
Thanks in advance.
You have a couple issues. First, as you suspected, was the incorrect use of erase. When you erase an iterator it invalidates the iterator. Any use of the iterator afterwards is undefined behavior. Since erase returns the next valid iterator what you can do is restructure the loop like
for (START = l.begin(); START != l.end();) { // do not increment here
if (*START) {
counter++;
START = l.erase(START); // erase and get next
}
else
{
++START; // go to next
}
}
So now at least you loop through the list. Unfortunately you will still have an invalid iterator in main. You pass START from main to countRepetition and when that iterator is erased from the list you then have an invalid iterator. What you need to do is get a new begin iterator from the list each iteration since you are always erasing the first element. That would make your for loop look like
for (START = l.begin(); START != l.end(); START = l.begin()) {
m.push_back(countRepetition(START));
}
Another issue is you just check if the character is not 0. If you are counting repetitions you need to make sure you are checking that the iterator is the same character. I'll leave that for you to implement.
I would also like to point out there is an easier way to do all of this. A std::map lets you build a histogram very easily. Combine that with std::max_element and you could write your entire program as
int main()
{
std::map<char, int> histogram;
while ('0' != (number = getchar()))
++histogram[number]; // add to map, increment count of occurances
auto most_frequent = *std::max_element(histogram.begin(),
histogram.end(),
[](const auto& lhs, const auto& rhs) { return lhs.second < rhs.second; }).first;
std::cout << most_frequent;
return 0;
}
Your problem is that you use global variables everywhere.
The global START is changed in two loops, so you only access the first loop once, then it is changed again in the second function and you don't execute the first loop a second time.
Why do you use the global variables? You should not use them but use local variables.
This is probably what you are looking for:
#include <iostream>
#include <list>
#include <vector>
#include <map>
using namespace std;
list <char> l;
map<char, int> ans;
int main()
{
char c;
do{
c = getchar();
l.push_back(c);
}while(c != '0');
for(auto chr: l){
ans[chr]++;
}
char ch;
int mx = 0;
for(auto k: ans){
if(k.second > mx)
{
ch = k.first;
mx = k.second;
}
}
cout<<ch<<" : "<<mx;
}

C++ changing order of vector using iter_swap

I'm passing in a vector with 20 items to the function below. My aim is to change the order of the items in the vector. If the list contains the items 1 2 3 4, I want the end result to be 4 3 2 1. Basically switching the first item with the last, and the second item with the second to last item.
template<typename T>
void changePosition(std::vector<T>& list)
{
std::cout << "Swapping \n";
for (int i = 0; i < list.size(); i++)
{
std::iter_swap(list.begin() + i, list.end() - i);
}
std::cout << "Swapped \n";
}
I just end up getting the error:
c++ vector iterator not dereferencable
Any ideas? I haven't found any samples that achieve what I'm trying to achieve.
std::reverse could indeed be used, the assignment I'm working on requires me to use iter_swap.
list.end() points one past the last element of the vector. So on the first iteration of your loop, list.end() - i will point one past the end, and cannot be dereferenced (or passed to iter_swap).
You should use list.end() - i - 1 instead so that it starts at the last element.
But once you fix this, you will run into another problem: Your code swaps each element twice, so nothing will change. You should stop at the middle of the vector to fix this.
Of course, the best way to reverse a vector is to use the standard function std::reverse instead of reinventing the wheel.
Maybe you should just use std::reverse
I managed to solve the issue with the following:
template<typename T>
void changePosition(std::vector<T>& list)
{
std::cout << "Swapping \n";
typedef std::vector<T>::iterator iterator;
iterator first = list.begin();
iterator last = list.end();
while ((first != last) && (first != --last))
{
std::iter_swap(first, last);
++first;
}
std::cout << "Swapped \n";
}
I doubt this is the best way to go about things but it got the job done. std::reverse should indeed be used by others trying to reverse a vector.
The only reason I resort to iter_swap is because of the assignment I'm working on requiring it.
You can use iter_swap . In fact std::reverse also uses iter_swap
#include<vector>
#include<iostream>
template<class BidirIt>
void reverseVector(BidirIt first, BidirIt last)
{
while ((first != last) && (first != --last)) {
std::iter_swap(first++, last);
}
}
int main()
{
std::vector<int> vec{1,2,3,4};
for(auto v:vec)
{
std::cout<<v<<" ";
}
std::cout<<"After reverting"<<"\n";
reverseVector(vec.begin(),vec.end());
for(auto v:vec)
{
std::cout<<v<<" ";
}
}
Output
1 2 3 4 After reverting
4 3 2 1 Program ended with exit code: 0

Checking every element of vector with functor

I'm trying to detect whether every single element of the vector is fullfilling given condition, let's say it must even number.
#include <iostream>
#include <vector>
#include <algorithm>
bool isOdd(int i)
{
return i%2==0;
}
int main()
{
int arr[5]={1,2,3,4,5};
std::vector<int> myVec(arr, arr + sizeof(arr)/sizeof(arr[0]));
std::vector<int>::iterator it = std::find_if(myVec.begin(), myVec.end(),
isOdd());
// This piece of code is probably causing some problems;
while(myVec.empty()!=false) // while my vector IS NOT EMPTY
{
std::cout << *it << " "; // print out the value of elements that
// fullfiled the condition given in isOdd
}
return 0;
}
What is wrong with my way of thinking ? Is the condition in while loop wrong or maybe I've completely missed the logic ?
Can you please provide me with some complex explanation of what is wrong with this piece of code ?
Thank you in advance.
P.S. I know that there is a possibility to use lambda function instead, but I don't want to get too confused :)
The problem with your approach is that you are finding the odd number only once, and then for some reason you expect the vector to change, without making any modifications.
You should make a loop that calls find_if repeatedly, like this:
bool isOdd(int i) {
return i%2!=0;
}
...
std::vector<int>::iterator it = myVec.begin();
for (;;) {
it = std::find_if(it, myVec.end(), isOdd);
if (it == myVec.end()) {
break;
}
std::cout << *it << " ";
++it;
}
Demo.
Note: I changed your isOdd function to return true for odd numbers. The original version was returning true for even numbers.
find_if returns the iterator pointing to the first value which meets the given condition. It stops there. You can put this in a loop to find all such elements, until it returns the end iterator.
The following line does the exact opposite of what you meant:
while(myVec.empty()!=false) // while my vector IS NOT EMPTY
Either write
while(myVec.empty()==false)
or
while(myVec.empty()!=true)
or simpler
while(!myVec.empty())
You could write it as a for-loop:
for (auto it = find_if(begin(myVec), end(myVec), isOdd);
it != end(myVec);
it = find_if(it, end(myVec), isOdd))
{
// do something with "it"
}

Strange iterator behaviour

#include "stdafx.h"
int _tmain(int argc, _TCHAR* argv[])
{
string s = "Haven't got an idea why.";
auto beg = s.begin();
auto end = s.end();
while (beg < end)
{
cout << *beg << '\n';
if (*beg == 'a')
{//whithout if construct it works perfectly
beg = s.erase(beg);
}
++beg;
}
return 0;
}
Why if I erase one or more chars from this string this code breaks? I suppose it has something to do with returned iterator after erase operation being created at higher address than end iterator but I'm not sure and it surely isn't right behaviour. Or is it?
There are several problems with this code.
Don't cache the value of s.end(); it changes as you delete elements.
Don't use beg < end. The idiomatic approach is to write beg != end. If you try to iterate past end, the result is undefined, and a debug version of the string library may deliberately crash your process, so it is meaningless to use <.
The iterator returned from s.erase(beg) might be s.end(), in which case ++beg takes you past the end.
Here's a (I think) correct version:
int _tmain(int argc, _TCHAR* argv[])
{
string s = "Haven't got an idea why.";
for (auto beg = s.begin(); beg != s.end();)
{
cout << *beg << '\n';
if (*beg == 'a')
{//whithout if construct it works perfectly
beg = s.erase(beg);
}
else
{
++beg;
}
}
}
EDIT: I suggest accepting FredOverflow's answer. It is simpler and faster than the above.
Erasing elements one by one from vectors or strings has quadratic complexity. There are better solutions with linear complexity:
#include <string>
#include <algorithm>
int main()
{
std::string s = "Haven't got an idea why.";
s.erase(std::remove(s.begin(), s.end(), 'a'), s.end());
std::cout << s << std::endl;
}
The previous s.end() value stored in end is invalid after s.erase(). Hence, do not use it.
Note the semantics of a basic_string and it's iterators.
From www.ski.com/tech/stl
Note also that, according to the C++ standard, basic_string has very unusual iterator invalidation semantics. Iterators may be invalidated by swap, reserve, insert, and erase (and by functions that are equivalent to insert and/or erase, such as clear, resize, append, and replace). Additionally, however, the first call to any non-const member function, including the non-const version of begin() or operator[], may invalidate iterators. (The intent of these iterator invalidation rules is to give implementors greater freedom in implementation techniques.)
Also what happens if
beg = s.erase(beg);
Returns an iterator equivalent to end()
On calling erase operation, stored end iterator pointer becomes invalid. So, use s.end() function in while loop condition
You have to iterate from .end()-1 to .begin(). At the same time, it is not safe to use comparison operators other than == and !=.
Here's my code:
vector<long long> myVector (my, my+myCount);
//sort and iterate through top correlation data counts
sort (myVector.begin(), myVector.end());
cout << endl;
int TopCorrelationDataCount = 0;
bool myVectorIterator_lastItem = false;
vector<long long>::iterator myVectorIterator=myVector.end()-1;
while (true) {
long long storedData = *myVectorIterator;
cout << TopCorrelationDataCount << " " << storedData << endl;
//prepare for next item
TopCorrelationDataCount++;
//if (TopCorrelationDataCount >= this->TopCorrelationDataSize) break;
if (myVectorIterator_lastItem) break;
myVectorIterator--;
if (myVectorIterator==myVector.begin())
{
myVectorIterator_lastItem = true;
}
}
Note: It can't be done using an ordinary for, because you have to find out if ==.begin(). If it is, this will be your last iteration. You can't check if ==.begin()-1, as it will result in run time error.
If you only want to use to X items in a vector, use TopCorrelationDataCount.

List Iterator Remove()

I have a list iterator that goes through a list and removes all the even numbers. I can use the list iterator to print out the numbers fine but I cannot use the list's remove() and pass in the dereferenced iterator.
I noticed that when the remove() statement is in effect, *itr gets corrupted? Can somebody explain this?
#include <iostream>
#include <list>
#define MAX 100
using namespace std;
int main()
{
list<int> listA;
list<int>::iterator itr;
//create list of 0 to 100
for(int i=0; i<=MAX; i++)
listA.push_back(i);
//remove even numbers
for(itr = listA.begin(); itr != listA.end(); ++itr)
{
if ( *itr % 2 == 0 )
{
cout << *itr << endl;
listA.remove(*itr); //comment this line out and it will print properly
}
}
}
There are a few issues with your code above. Firstly, the remove will invalidate any iterators that are pointing at the removed elements. You then go on to continue using the iterator. It is difficult to tell which element(s) remove would erase in the general case (although not in yours) since it can remove more than one.
Secondly, you are probably using the wrong method. Remove will iterate through all of the items in the list looking for any matching elements - this will be inefficient in your case because there is only one. It looks like you should use the erase method, you probably only want to erase the item at the position of the iterator. The good thing about erase is it returns an iterator which is at the next valid position. The idiomatic way to use it is something like this:
//remove even numbers
for(itr = listA.begin(); itr != listA.end();)
{
if ( *itr % 2 == 0 )
{
cout << *itr << endl;
itr=listA.erase(itr);
}
else
++itr;
}
Finally, you could also use remove_if to do the same as you are doing:
bool even(int i) { return i % 2 == 0; }
listA.remove_if(even);
You can't use an iterator after you delete the element it referred to.
However, list iterators which refer to non-deleted items after a remove() should remain valid.
Could we use something like this:
container.erase(it++);
I tried on this example:
int main(){
list<int>*a=new list<int>;
a->push_back(1);
a->push_back(2);
a->push_back(3);
list<int>::iterator I;
I=a->begin(); ++I;
a->erase(I++);
cout<<*I<<endl;
}
and it displayed 3, as I wanted. Now I don't know if this is valid or one of those which "sometimes work and sometimes not".
EDIT: Maybe it is because of compiler. For example, compiler I am using (GNU gcc-g++) is treating lists (std::) as circular, ie if I increase iterator after list->end() it puts you at the beginning.
Since iterators depend on the length of the structure remaining the same, most iterators do not allow a list to be changed while the iterator is in use. If you want to go through and change the list, you're going to have to use a loop independent of the iterator.