C++ changing order of vector using iter_swap - c++

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

Related

How can I pass iterators as function arguments?

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;
}

how to use iterator in a recursive function

im studying c++ primer, and in one exercise, i have to do a recursive function that prints the elements on a vector.
I did this:
void printVector(vector<int>::iterator it1, vector<int>::iterator it2) {
cout << *it1 << " ";
if (it1 != it2-1)
printVector((it1 + 1), it2);
}
is there another form to declare it, without the
if(it1!= ***IT2-1***)
i feel like its a mediocre solution couse i cant find another way.
thanks!!
Your function does not accept empty range, which it should, and it is a good idea to put exit condition into begin of a recursive function:
void printVector(vector<int>::iterator it1, vector<int>::iterator it2)
{
if( it1 == it2 ) return;
cout << *it1++ << " ";
printVector(it1, it2);
}
Yes.
void printVector(vector<int>::iterator begin, vector<int>::iterator end)
{
if (begin != end) {
cout << *begin << " ";
printVector(++begin, end)(;
}
}
Yes, you probably want to go at it in another way. It is pretty silly to do recursion when a simple loop will do. Instead, recursion is a tool to use in a more divide an conquer style. I.e. divide into two parts, then apply the same function to the first part and then to the second part.
Usually you then have some kind of cutoff point as to when you can actually do whatever it is you want to do, say for instance that you have less than N number of elements to work with or similar. This example is pretty contrived, because it only brings overhead to do it recursively.
template<class Iter>
void printVector(Iter begin, Iter end)
{
const auto dist = std::distance(begin, end);
if (0 == dist) {
return;
} else if (1 == dist) {
std::cout << *begin;
} else {
// Find the middle
auto pivot = begin + dist/2;
// Apply to first part
printVector(begin, pivot);
// Print separator
std::cout << " ";
// Apply to second part
printVector(pivot, end);
}
}
Please forgive any typos and other issues. Oh, I made it a template as well so that it accepts any random access iterators. That was mostly because it felt annoying to type vector<int>::iterator twice. That should probably be vector<int>::const_iterator by the way.

How can I skip elements in a range-based for loop based on 'index'?

Is there a way to access the iterator (I suppose there's no loop index?) in a C++11 range-based for loop?
Often we need to do something special with the first element of a container and iterate over the remaining elements. So I'm looking for something like the c++11_get_index_of statement in this pseudo-code:
for (auto& elem: container)
{
if (c++11_get_index_of(elem) == 0)
continue;
// do something with remaining elements
}
I'd really like to avoid going back to old-style manual iterator handling code in that scenario.
Often we need to do something special with the first element of a
container and iterate over the remaining elements.
I am surprised to see that nobody has proposed this solution so far:
auto it = std::begin(container);
// do your special stuff here with the first element
++it;
for (auto end=std::end(container); it!=end; ++it) {
// Note that there is no branch inside the loop!
// iterate over the rest of the container
}
It has the big advantage that the branch is moved out of the loop. It makes the loop much simpler and perhaps the compiler can also optimize it better.
If you insist on the range-based for loop, maybe the simplest way to do it is this (there are other, uglier ways):
std::size_t index = 0;
for (auto& elem : container) {
// skip the first element
if (index++ == 0) {
continue;
}
// iterate over the rest of the container
}
However, I would seriously move the branch out of the loop if all you need is to skip the first element.
Boost provides a nice succinct way to do this:
std::vector<int> xs{ 1, 2, 3, 4, 5 };
for (const auto &x : boost::make_iterator_range(xs.begin() + 1, xs.end())) {
std::cout << x << " ";
}
// Prints: 2 3 4 5
You can find make_iterator_range in the boost/range/iterator_range.hpp header.
How about using a simple for loop with iteratos:
for(auto it = container.begin(); it != container.end(); it++)
{
if(it == container.begin())
{
//do stuff for first
}
else
{
//do default stuff
}
}
It's not range based, but it's functional.
In case you may still want to use the range loop:
int counter = 0;
for(auto &data: container)
{
if(counter == 0)
{
//do stuff for first
}
else
{
//do default stuff
}
counter++;
}
No, you can't get the iterator in a range-based for loop (without looking up the element in the container, of course). The iterator is defined by the standard as being named __begin but this is for exposition only. If you need the iterator, it is intended that you use the normal for loop. The reason range-based for loop exists is for those cases where you do not need to care about handling the iteration yourself.
With auto and std::begin and std::end, your for loop should still be very simple:
for (auto it = std::begin(container); it != std::end(container); it++)
When iterating over elements, always prefer to use an algorithm, and use a plain for loop only if none of the algorithms fit.
Picking the right algorithm depends on what you want to do with the elements... which you haven't told us.
If you want to skip the first element, dump example:
if (!container.empty()) {
for_each(++container.begin(), container.end(), [](int val) { cout << val; });
}
There is no way of knowing how far an element is within the container without having an iterator, pointer or an intrusive index. Here's a simple way of doing it:
int index= 0;
for (auto& elem: container)
{
if (index++ == something)
continue;
// do something with remaining elements
}
If you want to skip the first element, another way is to use a std::deque and pop_front the first element. Then you can do your ranged for loop with the container as usual.
When I need to do something like this on a random access container, my habit is to iterate over the indexes.
for( std::size_t i : indexes( container ) ) {
if (i==0) continue;
auto&& e = container[i];
// code
}
the only tricky part is writing indexes, which returns a range of what boost calls counting iterators. Creating a basic iterable range from iterators is easy: either use boost's range concept, or roll your own.
A basic range for an arbitrary iterator type is:
template<typename Iterator>
struct Range {
Iterator b; Iterator e;
Range( Iterator b_, Iterator e_ ):b(b_), e(e_) {};
Iterator begin() const { return b; }
Iterator end() const { return e; }
};
which you can gussy up a bunch, but that is the core.
I would try to avoid using iterators, because the idea of a range-based for loop is to get rid of them. As of C++20, to skip the first element in your container, I would take one of the following approaches. I also include, for the sake of completeness, how to handle the first element separately:
Handling the first element outside the loop
You can use container.front() which exists for all sequence containers to access the first element. However, you must make sure that the container is not empty to avoid a segmentation fault. Then, to skip the first element (or more) in the loop, you can use the range adapter std::views::drop from the Ranges library. All together it looks as follows:
std::vector<int> container { 1, 2, 3 };
if(!container.empty()) {
// do something with first element
std::cout << "First element: " << container.front() << std::endl;
}
for (auto& elem : container | std::views::drop(1)) {
// do something with remaining elements
std::cout << "Remaining element: " << elem << std::endl;
}
Instead of container.front() you can also use another range-based for loop together with the range adapter std::views::take(1). The advantage of take() and drop() is that they work safely even if their arguments exceed the count of elements in your container.
Handling the first element inside the loop
You can use an init-statement in a range-based for loop to define a Boolean flag (or even a counter). This way, the flag is visible only within the scope of the loop. You can use the flag inside the loop as follows:
std::vector<int> container { 1, 2, 3 };
for(bool isFirst(true); auto& elem : container) {
if(isFirst) {
// do something with first element
std::cout << "First element: " << elem << std::endl;
isFirst = false;
continue;
}
// do something with remaining elements
std::cout << "Remaining element: " << elem << std::endl;
}
Output for both approaches shown:
First element: 1
Remaining element: 2
Remaining element: 3
Code on Wandbox

Standard library partition algorithm

I wrote this partition function:
template <class I, class P> I partition(I beg, I end, P p)
{
I first = beg;
while(beg != end) {
if(!p(*beg))
beg++;
else {
// if(beg != first) - EDIT: add conditional to prevent swapping identical elements
std::swap(*beg, *first);
first++;
beg++;
}
}
return first;
}
I've tested it with a few outputs and I haven't found anything wrong with it.
The standard library partition function is equivalent to:
template <class BidirectionalIterator, class UnaryPredicate>
BidirectionalIterator partition (BidirectionalIterator first,
BidirectionalIterator last, UnaryPredicate pred)
{
while (first!=last) {
while (pred(*first)) {
++first;
if (first==last) return first;
}
do {
--last;
if (first==last) return first;
} while (!pred(*last));
swap (*first,*last);
++first;
}
return first;
}
The latter seems much more complicated and has nested loops. Is there something wrong with my version? If not why the more complicated version?
Here is some output using the following predicate:
bool greaterthantwo(double val)
{
return val > 2;
}
MAIN
std::vector<double> test{1,2,3,4,2,5,6,7,4,8,2,4,10};
std::vector<double>::iterator part = ::partition(test.begin(), test.end(), greaterthantwo);
for(const auto &ref:test)
std::cout << ref << " ";
std::cout << std::endl;
for(auto it = part; it != test.end(); it++)
std::cout << *it << " ";
std::cout << std::endl;
OUTPUT
3 4 5 6 7 4 8 4 10 2 2 2 1
2 2 2 1
Your version is close to Nico Lomuto partition. Such partition works on ForwardIterators and is semi-stable (first part is stable, which can be useful in some circumstances).
Version from implementation of standard library which you quoted is close to partition described by C. A. R. Hoare at his paper "Quicksort". It works on BidirectionalIterators, and does not imply any stability.
Let's compare them on following case:
FTTTT
Forward partition will proceed like this:
FTTTT
TFTTT
TTFTT
TTTFT
TTTTF
resulting in swap on each iteration except first, while Bidirectional partition will go thru following permutations:
FTTTT
TTTTF
resulting only in one swap for all iterations.
Moreover, in general case Bidirectional will do N/2 swaps at maximum, while Forward version can do up to ~N swaps.
std::partition in C++98/03 works on BidirectionalIterators, but in C++11 they relaxed requirements to ForwardIterators (though, it doesn't have to be semi-stable). Complexity requirements:
Complexity: If ForwardIterator meets the requirements for a BidirectionalIterator, at most (last -first) / 2 swaps are done; otherwise at most last - first swaps are done. Exactly last - first applications of the predicate are done.
As you can see, implementations of standard library most likely will use Lomuto's partition for ForwardIterators and Hoare's partition for BidirectionalIterators.
Alexander Stepanov discuses partition problem in his Notes on Programming and in Elements of Programming co-authored with Paul McJones
Live Demo
#include <initializer_list>
#include <forward_list>
#include <algorithm>
#include <iostream>
#include <iterator>
#include <list>
using namespace std;
int counter = 0;
struct T
{
int value;
T(int x = 0) : value(x) {}
T(const T &x)
{
++counter;
value = x.value;
}
T &operator=(const T &x)
{
++counter;
value = x.value;
return *this;
}
};
auto pred = [](const T &x){return x.value;};
template<typename Container>
void test()
{
Container l = {0, 1, 1, 1, 1};
counter = 0;
partition(begin(l), end(l), pred);
cout << "Moves count: " << counter << endl;
}
int main()
{
test<forward_list<T>>();
test<list<T>>();
}
Output is:
Moves count: 12
Moves count: 3
(swap is 3 moves)
Your function has a serious defect. It swaps each element that satisfies the predicate with itself if initial elements of the sequence satisfy the predicate.
From STL partition description
Complexity
Linear in the distance between first and last: Applies pred to each element, and possibly swaps some of them (if the iterator type is a bidirectional, at most half that many swaps, otherwise at most that many).
In your implementation you swap more.

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.