Trouble in understading behaviour of std::transform - c++

Consider this -
#include <iostream>
#include <algorithm>
#include <functional>
#include <iterator>
#include <list>
int main()
{
typedef std::list<int> L;
L l(5);
typedef L::const_iterator CI;
typedef L::iterator I;
CI cb = l.begin(), ce = l.end();
I b = l.begin();
std::transform(cb, --ce, ++b, std::bind2nd(std::plus<CI::value_type>(), 1));
std::copy(l.begin(), l.end(), std::ostream_iterator<CI::value_type>(std::cout\
));
std::cout<<std::endl;
return 0;
}
Acccording to me its output should be 01111 as transform will iterate from first to "second last" element of list add 1 to each element and overwrite from "2nd" to "last" element.
But to my surprise output is 01234
What am I missing ? I read the documentation of transform on cppreference

This is because your are storing the result of each individual transformation into the next element of the source container, so the next input of the transform function will be the output of the previous transform (because of how you defined the input and output ranges).
In other words, the transform() algorithm will process the first element of your container (0), add 1 to it, and store the result (1) as the second element of that same container. Then, it will process the second element, which is now 1; it will add 1 to it, and store the result (2) as the third element of the container. And so on until you get to the last iteration.
If you want to get the output 01111, you should either store the result of the transform into a diffferent container (live example):
std::vector<int> out(l.size());
std::transform(cb, --ce, ++out.begin(),
// ^^^^^^^^^^^^^
std::bind2nd(std::plus<CI::value_type>(), 1));
Or use a different iteration strategy, as suggested by DyP in the comments (live example):
std::transform(++cb, ce, ++b, std::bind2nd(std::plus<CI::value_type>(), 1));

Since you are starting to add elements into the same container at ++b, there is an off by one offset, which means that you insert element i plus 1 into position i+1. So you are accummulating the sum as you move along. Assuming transform processes the elements sequentially, this is what the first three iterations will do:
0 0 0 0 0
Add 1 to position 0, put it in position 1
0 1 0 0 0
Add 1 to position 1, put it in position 2
0 1 2 0 0
Add 1 to position 2, put it in position 3
0 1 2 3 0

Related

Adjusting index in a vector in C++

I am writing a code in which the values of vector indexes are displayed as per the sorted order of the elements they hold:
For example:
values -> 3 2 4 1 5
indices -> 1 2 3 4 5 //yeah I know C++ indexing starts with 0. Well, while printing, I will add 1
result -> 4 2 1 3 5 //Bear with me. I know its confusing. I will clarify below
Now, the result has been obtained by sorting the elements and printing their earlier indices.
Like:
values(sorted) -> 1 2 3 4 5
indices(before sorting) -> 4 2 1 3 5
Now, there are many ways to do it with some suggesting to store the previous values and search and print the previous index, while others suggesting to create a new vector and copy the previous indices in it and then sorting them and.... (Well I didn't read further, 'cause that's definitely not how I was gonna proceed)
I tried a different approach while trying to not use a second vector.
So here's the approach:
while (!vec_students.empty()) {
std::vector<int>::iterator iterator = std::min_element(vec_students.begin(), vec_students.end());
std::cout << std::distance(vec_students.begin(), iterator) + 1 << " ";
vec_students.erase(iterator);
}
Now in this approach, the problem I am facing is that once I use erase, the index of all elements decreases by a certain incrementing value. So this was the solution I thought of:
while (!vec_students.empty()) {
static int i = 0; //yeah I know standard static variables are initialised to 1.
std::vector<int>::iterator iterator = std::min_element(vec_students.begin(), vec_students.end());
std::cout << std::distance(vec_students.begin(), iterator) + i << " ";
vec_students.erase(iterator);
i++;
}
Now the thought goes like this:
Initial Solution:
vector: 2 3 1
expected output: 3 1 2 (For explanation refer above)
first index = indexof(min(2,3,1)) -> 2 (while printing add 1) -> 3
second index = indexof(min(2,3)) -> 0 (while printing....) -> 1
third index = indexof(min(3)) -> 0 (while...) -> 1
Then I realized that the vector size decreases which means, indices will decrease (by 1 in this case)
so I added the extra i thingy.
Solution working:
vector: 2 3 1 i = 0
first index = indexof(min(2,3,1)) -> 3 -> add i -> 3 -> increment i -> i = 1
second index = indexof(min(2,3)) -> 0 -> add i -> 1 -> increment i -> i = 2
third index = indexof(min(3)) -> 0 -> add i -> 2 -> increment i -> i = 3
and the program ends.
But in the above case, instead of 3 1 2 I am getting 3 2 3 (first value right, rest incremented by 1)
What is wrong with my logic?
the index of all elements decreases by a certain incrementing value.
Not all, just the ones that come after the one you removed. Here's one way to do it without making another vector:
#include <algorithm>
#include <iostream>
#include <limits>
#include <vector>
int main() {
std::vector<int> v{3, 2, 4, 1, 5};
auto const* beg = v.data();
auto sz = v.size();
while (sz--) {
auto const min = std::min_element(v.begin(), v.end());
std::cout << &*min - beg << ' ';
*min = std::numeric_limits<int>::max();
}
}
This won't work properly if you have INT_MAX in your vector. In any case, making a second vector could yield better solutions. An example:
#include <algorithm>
#include <iostream>
#include <iterator>
#include <vector>
int main() {
std::vector<int> v{3, 2, 4, 1, 5};
std::vector<int const*> addresses;
addresses.reserve(v.size());
std::transform(v.cbegin(), v.cend(), std::back_inserter(addresses),
[](auto const& elm) { return &elm; });
std::sort(addresses.begin(), addresses.end(),
[](int const* const ptr1, int const* const ptr2) {
return *ptr1 < *ptr2;
});
for (auto* p : addresses) {
std::cout << p - v.data() << ' ';
}
}
You think you should add indices because the vector is shrink.
Well, not really, only those after the removed element should be effected.
example.
[2,1,3] => [2,3]
index of 2 remains at 0,
while index of 3 becomes 1 from 2

How to use boost::range::adaptors::transformed over std::unordered_set?

I'm trying to use boost::adaptors::transformed over a std::unordered_set but seems to produce weird behaviors even on quite small experiments.
I'm using Boost 1.58.0 on Ubuntu 16.04 with gcc 5.4.0.
Added elements after range initialization are not listed while iterating the range:
#include <iostream>
#include <vector>
#include <unordered_set>
#include <boost/range/adaptor/transformed.hpp>
struct double_int
{
typedef int result_type;
int operator()(int x) const { return x * 2; }
};
int main()
{
std::unordered_set<int> set;
for(int i = 0; i < 5; ++i)
set.insert(i); //adding ints to set
auto range = set | boost::adaptors::transformed(double_int());
set.insert(10); //adding some other int
//this produces: '8 0 2 4 6'
for(auto i : range)
std::cout << i << std::endl;
//this produces: '10 4 0 1 2 3'
for(auto i : set)
std::cout << i << std::endl;
//element 10 is not doubled!
return 0;
}
Following the same scheme with other std containers (like std::list) work as intended, doubling the latter added elements.
Even more weirdly if the set is initialized using:
std::unordered_set<int> set = {0,1,2,3,4,5};
range iteration gives only '10' while container's '10 0 1 2 3 4 5'
Could someone tell me what is wrong with this example?
transformed doesn't store a reference to the range as a range; it grabs the begin/end iterators of the range at the time of the range adaptor's construction.
When you later insert into the set after the adaptor's been constructed, the new element might not be in the range delimited by the old begin/end iterators prior to the insertion. Or, even worse, the insertion will invalidate all the iterators if it triggers a rehash.
I can't really see the problem:
Your exact sample code (live) prints:
8 20 6 4 0 2
4 10 3 2 0 1
Which seems to be what you should expect.
Using the other elements:
10 8 6 4 2 0
5 4 3 2 1 0
IDEA:
It could be Undefined Behaviour to assign the template expression:
auto range = set | boost::adaptors::transformed(double_int());
Because double_int() might be kept in the transformed adaptor BY REFERENCE (haven't checked).
See whether this removes the issue for you:
for (auto i : set | boost::adaptors::transformed(double_int()))
std::cout << i << " ";

STL approaches for given loop

this loop works fine as expected. however is there any STL approach to mimic the exact functionality as the example below?
for (auto i = vec.size() - 1; i > 0; --i)
{
vec[i] = vec[i - 1];
}
Rather than an insertion or a rotate, all we're doing here is copying, so it seems like the thing to use is a copy. We could do the job with reverse_iterators:
std::copy(f.rbegin() + 1, f.rend(), f.rbegin());
...or with the algorithm really intended specifically for this sort of situation, std::copy_backward:
std::copy_backward(f.begin(), f.end()-1, f.end());
Either way, it's simple, straightforward, and about as efficient as possible (almost certainly more efficient than using insert/pop or rotate/assign).
std::rotate:
template< class ForwardIt >
ForwardIt rotate( ForwardIt first, ForwardIt n_first, ForwardIt last );
Used as (for a vector v)
// rotation left
std::rotate(v.begin(), v.begin() + 1, v.end());
// example:
// initial v: 1 2 3 4 5
// after rotate: : 2 3 4 5 1
// rotation right (as in your "script")
std::rotate(v.rbegin(), v.rbegin() + 1, v.rend());
// example:
// initial v: 1 2 3 4 5
// after rotate: 5 1 2 3 4
//now if you do this,then it'll have the same effect as your code.
v[0] = v[1];
//before assignment: 5 1 2 3 4
//after assignment: 1 1 2 3 4
The difference w.r.t. your example is that, here, the first element will receive the previously last element (whereas in your code, the first element is untouched).
Performs a left rotation on a range of elements.
Specifically, std::rotate swaps the elements in the range [first,
last) in such a way that the element n_first becomes the first element
of the new range and n_first - 1 becomes the last element.
A precondition of this function is that [first, n_first) and [n_first,
last) are valid ranges.
http://en.cppreference.com/w/cpp/algorithm/rotate
The exact equivalent? Assuming the vector is not empty:
auto val = vec.front(); //Just in case the list is 1 element long.
vec.pop_back();
vec.insert(vec.begin(), val);
Your code effectively does this:
1 2 3 4 5 6
1 1 2 3 4 5
The first element is in two places, while the last element is lost. The above code does the same.

what does [] const_iterator::value_type in std::transform mean

specifically here is the code. What is line 15 doing (call to transform)?
Can someone please explain why this outputs 01234? On the other hand if I change cb to ++cb in line 15, it outputs 01110. What is the return value on line 15 doing?
#include <algorithm>
#include <functional>
#include <iostream>
#include <iterator>
#include <list>
int main()
{
typedef std::list<int> L;
L l(5);
typedef L::const_iterator CI;
CI cb = l.begin(), ce = l.end();
typedef L::iterator I;
I b = l.begin();
std::transform(cb, --ce, ++b, [] (CI::value_type n) { return ++n; });
std::copy(l.begin(), l.end(), std::ostream_iterator<CI::value_type>(std::cout));
std::cout << std::endl;
return 0;
}
The expression [](CI:value_type n) { return ++n; }
is a lambda function. The empty brackets mean that it does not access any members of the current scope.
transform basically applies this function to each element of the input sequence (l) and writes it to the output sequence (also l). It stops when the element before the last one has been reached because of the --ce.
The code takes one element of l after the other and increments it into the next element of l (because of the ++b). Therefore you get 0, 1, 2, 3, 4.
If you change cb to ++cb, you get 0, 1, 1, 1, 0 because then, you start at the element with index 1 and just increment each one until the before last one.
Find some information about lambdas here.
Explanation of std::transform
In this statement
L l(5);
there is created a list of 5 elements each of which is initialized by 0.
In this call
std::transform(cb, --ce, ++b, [] (CI::value_type n) { return ++n; });
cb points to the first element of the list. --ce after evaluation the decrement operator points to the last element of the list.
Thus cb and --ce set range of elements of the list
[cb, --ce)
where the parenthesis means that --ce is not included in the range.
++b after evaluation of the increment points to the second element of the list.
So you have
b
|
0 0 0 0 0
^ ^
| |
cb ce
The value pointed to by cb that is the value of the first element of the list is increased in the lambda expression
[] (CI::value_type n) { return ++n; }
and is written in the second element of the list pointed to by iterator b. After that cb and b are incremented within the body of transform.
So after the first iteration the list looks like
b
|
0 1 0 0 0
^ ^
| |
cb ce
Now cb points to the second element of the list. Its value is incremented in the lambda expression and is written in the third element pointed to by iterator b.
b
|
0 1 2 0 0
^ ^
| |
cb ce
As a result you will get that list will have values 0, 1, 2, 3, 4.
If you will write the call of the algorithm like
std::transform(++cb, --ce, ++b, [] (CI::value_type n) { return ++n; });
that is using ++cb then cb and b will point to the same element and the algorithm simply will rewrite each element with its incremented value starting from the second element of the list because there was used iterator ++cb.
The result will be 0, 1, 1, 1, 0
First, you need to get through the syntax: square brackets indicate that your lambda function is not capturing anything from its surrounding context. Basically, this is a succinct way of plugging in a piece of logic into the call of std::transform: you tell the function that transforming a value means adding one to it.
To further understand what is going on, and also to explain the 01110 output, let's see what std::transform does: it takes items from cb (the initial element) up to --ce (the second element from the back), inclusive, calls the lambda function, and places the result it returns into cells starting at ++b, i.e. at indexes 1, 2, 3, and so on.
The first iteration takes zero from L[0], adds one, and writes 1 into L[1]. The second iteration picks up the 1 from before, adds one, and writes 2 into L[2]. The iteration continues until std::transform writes 4 into L[4].
When you replace cb with ++cb, however, the writing is performed into the same cell from which the data was read, i.e. L[1] gets assigned 0+1, then L[2] gets assigned 0+1, then L[3] gets assigned 0+1, and then the loop reaches --ce, and stops.
Note that ++n is unnecessary, because the side effect of incrementing n disappears as soon as the lambda call is over. You can replace it with n+1 expression, which has no side effect:
std::transform(cb, --ce, ++b, [] (CI::value_type n) { return n+1; });

Shifting values of a vector in C++

Assuming the following basic vector:
std::vector<int> numbers;
numbers.push_back(0);
numbers.push_back(1);
numbers.push_back(2);
numbers.push_back(3);
What is an efficient way to insert 6 new numbers in between 0 and 1?
Here is my approach right now, but I feel like it's not very efficient:
for (int new_number=0;new_number<6;new_number++) {
numbers.emplace(numbers.begin()+1+new_number,new_number);
}
The reason I don't like this approach is that numbers 1-3 have to be moved 6 times. Is there a way to move these numbers just once instead of doing it 6 times? Then I can use this loop:
for (int new_number=0;new_number<6;new_number++) {
numbers[new_number+1]=new_number;
}
Here is what I am trying to accomplish:
Vector Before Shifting:
0 1 2 3
Vector After Shifting:
0 X X X X X X 1 2 3
There is an overload of the insert function that lets you insert n identical items:
// Add six copies of -1 to the vector starting at position 1
numbers.insert(numbers.begin()+1, 6, -1);
You can use another overload that takes three iterators - the iterator where to insert, and a begin/end pair of iterators from where to take the data:
// Insert addedNUmbers at position 1
numbers.insert(numbers.begin()+1, addedNUmbers.begin(), addedNUmbers.end());
If you must insert items one-by-one from disparate sources, so you can't use the range-insertion, one option is to insert everything at the back, and then use std::rotate:
#include <algorithm>
v.reserve(v.size() + 6);
v.push_back(12);
v.push_back(foo());
// ...
std::rotate(v.begin() + 1, v.begin() + 4, v.end());
Something like this will insert 6 at the beginning of numbers:
numbers.insert(numbers.begin(), 6);
If you want to insert the number 6 after the number one:
numbers.insert(find(numbers.begin(), numbers.end(), 1), 6);
(Obviously, if you don't KNOW for sure that the number is in the list, you may want to check that before you insert!)