It is not possible in general to store different ranges in a homogeneous collection like std::vector
, because different ranges usually have different types, especially if transforms using lambdas are involved. No two lambdas have the same type and the type of the lambda will be part of the range type. If the signatures of the functions you want to pass to the transform are the same, you could wrap the lambdas in std::function
as suggested by @IlCapitano (https://godbolt.org/z/zGETzG4xW). Note that this comes at the cost of the additional overhead std::function
entails.
A better option might be to 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 i
th range represents the i
th vector's elements which have been multiplied by the i
th 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. Of course, you can always just evaluate part of the range, because the evaluation is lazy.
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::vector
s
- 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