有可能在cpp20中生成一个范围向量吗



假设我有一个vector<vector<int>>。我想以这样一种方式使用ranges::transform,以便获得

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

这样的转换,或者类似的东西,目前在C++中是可能的吗?

我知道简单地存储transformation_coeff是可能的,但在每一步应用它都很不方便。(这将重复多次,因此需要在O(log n)中完成,因此我不能显式应用转换(。

是的,您可以有一个范围向量。代码中的问题是在using语句中使用了临时lambda。正因为如此,稍后要推入向量的项目的类型与T不同。您可以通过首先将lambda分配给一个变量来解决它:

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

通常不可能在类似std::vector的同构集合中存储不同的范围,因为不同的范围通常有不同的类型,尤其是在涉及使用lambda的转换的情况下。没有两个lambda具有相同的类型,lambda的类型将是范围类型的一部分。如果要传递给转换的函数的签名相同,则可以按照@IlCapitano的建议将lambda封装在std::function中(https://godbolt.org/z/zGETzG4xW)。注意,这是以std::function所带来的额外开销为代价的。

一个更好的选择可能是创建一系列范围。

如果我理解正确的话,你有一个n矢量的矢量,例如

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

n系数的矢量,例如

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

并且您想要一个表示变换矢量的范围,其中第i个范围表示第i个矢量的元素,这些元素已乘以第i个系数:

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

我对你的理解正确吗?如果是,我不明白你对O(logn(的复杂性要求是什么意思。在这种情况下,n指的是什么?在不到n的步骤中,这种计算是如何实现的?这里有一个解决方案,可以为您提供所需的范围。计算这个范围需要O(n*m(次乘法,其中m是每个内部向量中元素数量的上界。我不认为这可以用更少的步骤完成,因为你必须将original_vector中的每个元素相乘一次。当然,您总是可以只评估范围的一部分,因为评估是懒惰的。

C++20

策略是首先为给定索引i的变换后的第i个向量创建一个范围。然后,您可以使用std::views::iota创建一个int范围,并将其转换为内部范围:

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

你现在可以进行

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

输出:

2 10 20 
2 4 8 
15 30 45 

Godbolt编译器Explorer 上的完整代码

C++23

这是C++23的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
);

它短了一点,还有一个额外的好处,即transformation_coeff也被视为一个范围:

  • 它更通用,因为我们不限于std::vectors
  • 在C++20解决方案中,如果transformation_coeff.size() < original_vector.size(),您将获得未定义的行为,而不需要额外的大小检查,因为我们正在索引到向量中,而C++23解决方案只会返回一个元素较少的范围

Godbold编译器浏览器上的完整代码

相关内容

  • 没有找到相关文章

最新更新