假设我有一个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::vector
s - 在C++20解决方案中,如果
transformation_coeff.size() < original_vector.size()
,您将获得未定义的行为,而不需要额外的大小检查,因为我们正在索引到向量中,而C++23解决方案只会返回一个元素较少的范围
Godbold编译器浏览器上的完整代码