I'm trying to get into 'modern' C++, so I'm trying to learn how to properly use functors, and, subsequently, lambdas.
I think I've understood basic principle behind it, but I'm having trouble to understand how to acquire any element from a vector that is passed to my algorithm.
So, let's say I wish to create a Fibonacci sequence of length N...
struct Adder {
int operator()(int a, int b) {return a+b;}
};
const int N = 10;
int main() {
std::vector<int> vec = {0, 1};
vec.resize(N);
//Old Way
for(int i = 2; i < vec.size(); i++) {
vec[i] = vec[i-1] + vec[i-2];
}
std::transform(vec.begin(), vec.end(), vec.begin(), [](int i){return i*3;}); //single operator given to function, works
// std::transform(vec.begin()+2, vec.end(), vec.begin(), /*here two calls are needed , for a and b operators*/);
return 0;
}
Basically my question is how to activate functor defined in struct Adder? What is the proper way to pass two operators to him?
Adder::operator() should be const. And your Adder functor is unnecessary. Just use std::plus<>.
Since C++17, we have the transform overload that accepts two sequences. So we can do: (you can use Adder{} in place of std::plus<>{} if you want)
std::transform(vec.begin(), vec.end() - 2, vec.begin() + 1, vec.begin() + 2, std::plus<>{});
Minimal example: (live demo)
#include <algorithm>
#include <iostream>
#include <vector>
constexpr int N = 10;
int main()
{
std::vector<int> vec{0, 1};
vec.resize(N);
std::transform(vec.begin(), vec.end() - 2, vec.begin() + 1, vec.begin() + 2, std::plus<>{});
for (int x : vec)
std::cout << x << " ";
std::cout << "\n";
}
Related
I have 2 vectors of ints.
say the first one has (2,1).
and the second one (1,1).
I am trying to subtract numbers like this:
2 - 1,
1 - 1
then I need to add these 2 numbers so the final answer would be 1.
I've tried a for loop, but it's subtracting each number from each element, instead of only the first one.
This is what I've tried so far.
vector<int> temp;
for(unsigned i =0; i < Vec1.size(); i++)
for(unsigned o =0; o < Vec2.size(); o++)
temp.push_back(Vec1.at(i).nums- Vec2.at(o).nums);
//where nums, are just the numbers showed above
The output as you would expect is :
1
1
0
0
and I need it to be:
1
0
then I can just do a for loop to add all the ints together.
Any help, would be greatly appreciated!
I've tried a for loop, but it's subtracting each number from each element, instead of only the first one.
You are not doing it the right way. You have been using cascaded for loops and hence, you are subtracting each element of first vector from each element of second vector.
There are two ways to correctly implement:
One involves writing your own functions to subtract two vector and then adding elements of the result.
#include <iostream>
#include <vector>
std::vector<int> subtract(const std::vector<int>& a, const std::vector<int>& b)
{
std::vector<int> result;
const int SIZE = std::min(a.size(), b.size());
for (int i = 0; i < SIZE; i++)
result.push_back(a[i] - b[i]);
return result;
}
int addAllElements(const std::vector<int>& a)
{
int result = 0;
for (auto i: a)
result += i;
return result;
}
int main(void)
{
std::vector<int> a = {2, 1};
std::vector<int> b = {1, 1};
std::cout << "Result is " << addAllElements(subtract(a, b)) << std::endl;
return 0;
}
The other method (preferred) involves using STL:
#include <iostream>
#include <vector>
#include <algorithm>
#include <numeric>
int main(void)
{
std::vector<int> a = { 2, 1 };
std::vector<int> b = { 1, 1 };
std::vector<int> result;
std::transform(std::begin(a), std::end(a), std::begin(b), std::back_inserter(result), [](const auto a, const auto b)
{
return a - b;
}
);
int sumAllElements = std::accumulate(result.begin(), result.end(), 0);
std::cout << "Result is " << sumAllElements << std::endl;
return 0;
}
The above code uses lambda expression. To know more about them, see this link.
std::accumulate sums all the elements of the container and std::transform performs the transformation (specified in it's fifth argument) on two vectors and put the result in a different vector. We have used lambda expression to perform the required sub operation.
EDIT:
To implement it without lambda is also easy. You can use function pointers.
#include <iostream>
#include <vector>
#include <algorithm>
#include <numeric>
double subtract(const double a, const double b)
{
return a - b;
}
int main(void)
{
std::vector<int> a = { 2, 1 };
std::vector<int> b = { 1, 1 };
std::vector<int> result;
std::transform(std::begin(a), std::end(a), std::begin(b), std::back_inserter(result), subtract);
int sumAllElements = std::accumulate(result.begin(), result.end(), 0);
std::cout << "Result is " << sumAllElements << std::endl;
return 0;
}
There are various advantages of using lambda expression.
NOTE:
You can also use std::minus instead of defining you own function. Like this:
std::transform(std::begin(a), std::end(a), std::begin(b), std::back_inserter(result), std::minus<int>());
In C++17, you can combine std::transform and std::reduce/std::accumulate calls with std::transform_reduce:
const std::vector<int> vec1 {2, 1};
const std::vector<int> vec2 {1, 1};
auto res = std::transform_reduce(vec1.begin(), vec1.end(),
vec2.begin(),
0,
std::plus<>(),
std::minus<>());
Demo
Here is an example using the STL:
#include <algorithm>
#include <iostream>
#include <numeric>
#include <vector>
int main() {
std::vector<int> vec1 {2, 1};
std::vector<int> vec2 {1, 1};
std::vector<int> temp;
std::transform(begin(vec1), std::end(vec1), std::begin(vec2),
std::back_inserter(temp), [](const auto a, const auto b) {return a - b;});
auto sum = std::accumulate(temp.begin(), temp.end(), 0);
std::cout << "Result: " << sum << "\n";
return 0;
}
Write a function which will take two arrays as parameters and add the individual
elements of each array together such that firstArray[i] = firstArray[i] +
secondArray[i] where 0 <= i <= 4.
int[] sumEqualLengthArrays(int[] a, int[] b) {
int[] result = new int[a.length];
for (int i = 0; i < a.length; i++)
result[i] = a[i] + b[i];
return result;
}
I have been stuck on this for a while now and I just can't get my head around what the answer is. I have attempted to answer it in the code above. I am a beginner to C++ programming as I am studying it in my free time. An answer to this question would really help!
Since you said you can use anything, Use std::vector with std::transform and std::plus<int>(). something like this :
std::transform (a.begin(), a.end(), b.begin(), a.begin(), std::plus<int>());
If you insist on using normal arrays (here assume a and b are arrays) then you can do something like this :
std::transform(a, &a[number_of_elements], b, a, std::plus<int>());
But please, don't.. Use std::vector.
How to use first approach :
#include <iostream>
#include <vector>
#include <algorithm>
int main()
{
std::vector<int> a = {1, 2, 3};
std::vector<int> b = {1, 2, 3};
std::transform(a.begin(), a.end(), b.begin(), a.begin(), std::plus<int>());
for(auto iter = a.begin(); iter != a.end(); ++iter)
{
std::cout << *iter << std::endl;
}
return 0;
}
How to use second approach :
#include <iostream>
#include <algorithm>
int main()
{
int a[3] = {1, 2, 3};
int b[3] = {1, 2, 3};
std::transform(a, &a[0] + 3, b, a, std::plus<int>());
for(int i = 0; i < 3; ++i)
{
std::cout << a[i] << std::endl;
}
return 0;
}
Something like this:
std::vector<int> sumEqualLengthArrays(const std::vector& rhs,
const std::vector& lhs){
if(lhs.length() == rhs.length()){
std::vector<int> result(rhs.length(), 0);
for(unsigned int i = 0; i < rhs.length; ++i){
result[i] = rhs[i] + lhs[i];
}
return result;
}else{
std::cout << "Length is not equal!" << std::endl;
return rhs;
}
}
I would advise to use vectors instead of arrays and check the length before usage just in case to avoid errors.
You've written the summing expression already in the problem formulation. If you look at it once again, you'll see that the result is stored in first and there's no need in separate result array (returning an array is not a trivial thing in C++).
And, speaking of which, passing arrays as arguments is not easy either.
Assuming, you don't use std::vector, simple options are as follows.
int a[] (note the position of square brackets) as a function formal argument is synonymous to a pointer. It does not contain any size information, so you'll have to add a third argument which is the minimal size of both arrays:
int *add(int a[], int b[], std::size_t commonSize) { // commonSize is the least of a's and b's sizes
for(std::size_t i = 0; i < commonSize; ++i) a[i] += b[i];
return a;
}
You can deduce array's size when passed by reference, this is allowed in C++ and is a serious deviation from classic C:
template<std::size_t A, std::size_t B> int (&add(int (&a)[A], int (&b)[B]))[A] {
for(std::size_t i = 0; i < std::min(A, B); ++i) a[i] += b[i];
return a;
}
Then the common size is the minimum of A and B.
You can use std::array, this is almost the same as previous option
template<std::size_t A, std::size_t B> void add(std::array<int, A> &a, std::array<int, B> const &b);
This way you can even use range-for loops, or, for instance, STL algorithms (which tend to acquire parallelized and non-sequential overloads recently), though it requires a small amount of additional work (which is a bit too large to fit in this margin).
If I have an integer vector (std::vector) is there an easy way to convert everything in that vector to a char array(char[]) so (1,2,3) -> ('1','2','3')
This is what I have tried, but it doesn't work:
std::vector<int> v;
for(int i = 1; i < 10; i++){
v.push_back(i);
}
char *a = &v[0];
std::transform is the right tool for the job :
std::vector<int> iv {1, 2, 3, 4, 5, 6};
char ca[10] {};
using std::begin;
using std::end;
std::transform(begin(iv), end(iv), begin(ca), [](int i) { return '0' + i; });
If you don't need ca to be a C-style array, I'd recommend using std::array or std::vector instead. The latter needs std::back_inserter(ca) in place of begin(ca).
It can be as simple as this
std::vector<int> v { 1, 2, 3 };
std::vector<char> c;
for( int i : v ) c.push_back( '0' + i );
to realize why your way does not work you need to learn how integers and symbls represented on your platform.
Why not store it in char vector initially?
std::vector<char> v;
for(int i = 1; i < 10; i++){
v.push_back(i + '0');
}
Trick from: https://stackoverflow.com/a/2279401/47351
You can use std::transform with std::back_inserter, maybe something like this:
#include <iostream>
#include <vector>
#include <algorithm>
int main()
{
std::vector<int> vI{0,1,2,3,4,5,6,7,8,9};
std::vector<char> vC;
std::transform(vI.begin(), vI.end(), std::back_inserter(vC), [](const int &i){ return '0'+i; });
for(const auto &i: vC)
{
std::cout << i << std::endl;
}
return 0;
}
If you've already got the numbers in a vector then you can use std::transform and a back_inserter:
std::vector<int> v;
std::vector<char> c;
std::transform(v.begin(), v.end(), std::back_inserter(c), [](int i){return '0' + i;});
However, why not just say:
std::vector<char> v;
for(char i = '0'; i < '9'; i++)
{
v.push_back(i);
}
This mean that while I am sorting the v2 in the nonincreasing order, the v1 should looks like this:
Vectors look as following.
v1 = {0, 5, 5, 2, 10};
v2 = {0 ,2, 6, 20, 5};
The output:
v1 = {2, 5, 10, 5, 0};
v2 = {20, 6, 5, 2, 0};
I was trying to solve that problem mixing std::sort and lambdas.
That is why I have read several questions about std::sort, there were no answers which solve the problem similar to mine so that is why I am asking.
I am especially interested in the answers which contains it's usage or other features of C++11 and C++14.
It is not a question like:
"I completely do not know what to do."
I know how to achieve the output using C++98, but I am wondering if there is an more efficient and prettier way to do it.
Big thanks for your help :)
You could zip, sort, and unzip.
#include <iostream>
#include <vector>
#include <algorithm>
//converts two vectors into vector of pairs
template <typename T, typename U>
auto zip(T t, U u) {
std::vector<std::pair<typename T::value_type,typename U::value_type>> pairs;
for (size_t i = 0; i < t.size(); ++i){
pairs.emplace_back(u[i],t[i]);
}
return pairs;
}
//converts a vector of pairs, back into two two vectors
template <typename T, typename U, typename V>
void unzip(V pairs, T & t, U & u) {
for (auto const& it: pairs){
u.emplace_back(it.first);
t.emplace_back(it.second);
}
}
int main(){
//vectors
std::vector<int> v1 = {0, 5, 5, 2, 10};
std::vector<int> v2 = {0 ,2, 6, 20, 5};
//zip vectors
auto pairs = zip(v1,v2);
//sort them
std::sort(pairs.begin(),pairs.end(),std::greater<>());
//unzip them
v1.clear();
v2.clear();
unzip(pairs,v1,v2);
//print
std::cout << '\n';
for (auto i: v1) std::cout << i << ' ';
std::cout << '\n';
for (auto i: v2) std::cout << i << ' ';
std::cout << '\n';
}
Well, I don't if this is would be efficient, or not, but this demonstrates how to do this with std::generate, std::sort, and std::transform, with some extra seasoning from mutable lambdas, and iterators.
#include <algorithm>
#include <iostream>
int main()
{
std::vector<int> v1={0, 5, 5, 2, 10},
v2 = {0, 2, 6, 20, 5};
std::vector<int> index;
index.resize(5);
std::generate(index.begin(), index.end(),
[n=0]
()
mutable
{
return n++;
});
std::sort(index.begin(), index.end(),
[&]
(auto &a, auto &b)
{
return v2[b] < v2[a];
});
std::vector<int> v1_out, v2_out;
std::transform(index.begin(), index.end(),
std::back_insert_iterator<std::vector<int>>(v1_out),
[&]
(auto a)
{
return v1[a];
});
std::transform(index.begin(), index.end(),
std::back_insert_iterator<std::vector<int>>(v2_out),
[&]
(auto a)
{
return v2[a];
});
for (auto n: v1_out)
std::cout << n << ' ';
std::cout << std::endl;
for (auto n: v2_out)
std::cout << n << ' ';
std::cout << std::endl;
}
Big thanks for all your answers. I found an easy way to achieve the same effect and the idea comes from this answer.
1.
Firstly I used all code from answer which I linked, so it is:
template <typename T>
vector<size_t> sort_indexes(const vector<T> &v)
{
// initialize original index locations
vector<size_t> idx(v.size());
for (size_t i = 0; i != idx.size(); ++i) idx[i] = i;
// sort indexes based on comparing values in v
sort(idx.begin(), idx.end(),
[&v](size_t i1, size_t i2)
{
return v[i1] >= v[i2];
});
return idx;
}
2.Then I did a function which will sort first and a second vector using the third one.To achieve it I had to create temporary vectors one for a first vector, one for a second, and two for the last one.
Why two?
Because I have to remember the sorted indexes of the third vector and I need a one temporary to which I will be pushing elements of the original third vector according to sorted indexes.
void SortByIndexes(vector<int>& Pi,vector<int> &Wi,vector<int>& PidivWi)
{
vector<int> Pitemp, Witemp, PidivWitemp,SortedIndexes;
for (auto i : sort_indexes(PidivWi))
{
SortedIndexes.push_back(i);
}
for (auto i : SortedIndexes)
{
Pitemp.push_back(Pi[i]);
Witemp.push_back(Wi[i]);
PidivWitemp.push_back(PidivWi[i]);
}
swap(Pi, Pitemp);
swap(Wi, Witemp);
swap(PidivWi,PidivWitemp);
}
3. After sorting just swap sorted vectors with original ones.
Done.
Thank you all guys.
I have a vector with some values. How can I copy it to another vector, so all the values but a specific one (located at position x - x will be a parameter of course) would be copied?
Moreover, I would like to use the value from location x for something else, so I prefer it will be saved.
Is there an easy way to do it?
How to copy stl vector except one specific value?
You can use std::copy_if:
std::vector<T> v = ....;
std::vector<T> out;
T x = someValue;
std::copy_if(v.begin(), v.end(), std::back_inserter(out),
[x](const T& t) { return t != x; });
If you don't have C++11 support, you can use std::remove_copy_if and adjust the predicate's logic accordingly.
use std::copy_if if you have c++11, otherwise:
void foo(int n) {
std::vector<int> in;
std::vector<int> out;
std::copy(in.begin(), in.begin() + n, out.end());
std::copy(in.begin() + n + 1, in.end(), out.end());
}
This works because std::vector has random-access iterators.
As Luchian suggests, you should use erase()
#include <vector>
#include <iostream>
#include<algorithm>
int main(){
std::vector<int> vec1;
vec1.push_back(3);
vec1.push_back(4); // X in your question
vec1.push_back(5);
std::vector<int> new_vec;
new_vec = vec1;
new_vec.erase(std::find(new_vec.begin(),new_vec.end(),4));
for (unsigned int i(0); i < new_vec.size(); ++i)
std::cout << new_vec[i] << std::endl;
return 0;
}
and for your second question, to determine the index of an element in a vector
// determine the index of 4 ( in your case X)
std::vector<int>::iterator it;
it = find(vec1.begin(), vec1.end(), 4);
std::cout << "position of 4: " << distance(vec1.begin(),it) << std::endl;