Home > OS >  Is it possible to make a vector of ranges in cpp20
Is it possible to make a vector of ranges in cpp20

Time:11-06

Let's say I have a a vector<vector<int>>. I want to use ranges::transform in such a way that I get

vector<vector<int>> original_vectors;
using T = decltype(ranges::views::transform(original_vectors[0], [&](int x){
              return x;
          }));
vector<int> transformation_coeff;
vector<T> transformed_vectors;
for(int i=0;i<n;i  ){
    transformed_vectors.push_back(ranges::views::transform(original_vectors[i], [&](int x){
        return x * transformation_coeff[i];
    }));
}

Is such a transformation, or something similar, currently possible in C ?

I know its possible to simply store the transformation_coeff, but it's inconvenient to apply it at every step. (This will be repeated multiple times so it needs to be done in O(log n), therefore I can't explicitly apply the transformation).

CodePudding user response:

Yes, you can have a vector of ranges. The problem in your code is that you are using a temporary lambda in your using statement. Because of that, the type of the item you are pushing into the vector later is different from T. You can solve it by assigning the lambda to a variable first:

vector<vector<int>> original_vectors;
auto lambda = [&](int x){return x;};
using T = decltype(ranges::views::transform(original_vectors[0], lambda));
vector<T> transformed_vectors;
transformed_vectors.push_back(ranges::views::transform(original_vectors[0], lambda));

CodePudding user response:

It is not possible to store different ranges in a homogeneous collection like std::vector, because different ranges also have different types. You can however create a range of ranges.

If I understand you correctly, you have a vector of n vectors, e.g.

std::vector<std::vector<int>> original_vector = {
    {1,  5, 10},
    {2,  4,  8},
    {5, 10, 15}
};

and a vector of n coefficients, e.g.

std::vector<int> transformation_coeff = {2, 1, 3};

and you want a range of ranges representing the transformed vectors, where the ith range represents the ith vector's elements which have been multiplied by the ith coefficient:

{
    { 2, 10, 20}, // {1,  5, 10} * 2
    { 2,  4,  8}, // {2,  4,  8} * 1
    {15, 30, 45}  // {5, 10, 15} * 3
}

Did I understand you correctly? If yes, I don't understand what you mean with your complexity requirement of O(log n). What does n refer to in this scenario? How would this calculation be possible in less than n steps? Here is a solution that gives you the range of ranges you want. Evaluating this range requires O(n*m) multiplications, where m is an upper bound for the number of elements in each inner vector. I don't think it can be done in less steps because you have to multiply each element in original_vector once.

C 20

The strategy is to first create a range for the transformed i-th vector given the index i. Then you can create a range of ints using std::views::iota and transform it to the inner ranges:

auto transformed_ranges = std::views::iota(0) | std::views::transform(
    [=](int i){

        // get a range containing only the ith inner range
        auto ith = original_vector | std::views::drop(i) | std::views::take(1) | std::views::join;

        // transform the ith inner range
        return ith | std::views::transform(
            [=](auto const& x){
                return x * transformation_coeff[i];
            }
        );
    }
);

You can now do

for (auto const& transformed_range : transformed_ranges){
    for (auto const& val : transformed_range){
        std::cout << val << " ";
    }
    std::cout<<"\n";
}

Output:

2 10 20 
2 4 8 
15 30 45 

Full Code on Godbolt Compiler Explorer

C 23

This is the perfect job for C 23's std::views::zip_transform:

auto transformed_ranges = std::views::zip_transform(
    [=](auto const& ith, auto const& coeff){
        return ith | std::views::transform(
            [=](auto const& x){
                return x * coeff;
            }
        );
    },
    original_vector,
    transformation_coeff
);

It's a bit shorter and has the added benefit that transformation_coeff is treated as a range as well:

  • It is more general, because we are not restricted to std::vectors
  • In the C 20 solution you get undefined behaviour without additional size checking if transformation_coeff.size() < original_vector.size() because we are indexing into the vector, while the C 23 solution would just return a range with fewer elements.

Full Code on Godbold Compiler Explorer

  • Related