是否可以使用动态数量的范围适配器?
Is it possible to use a dynamic number of range adaptors?
我对范围相当陌生,我想知道是否有办法应用动态数量的范围适配器。摸索了一段时间的代码,也搜索了一下,都没有结果。
#include <iostream>
#include <ranges>
int main() {
auto output = std::ranges::views::iota(2, 100);
for (int i = 2; i < 100; i++) {
output = output | std::ranges::views::filter([i](int num){ return num % i != 0 || num == i; });
}
std::cout << "The 10th prime is: " << output[9] << "\n";
}
本质上,我想要这样的东西,但这会产生编译错误(no match for 'operator='
)。好像每申请一个range adapter都需要一个新的类型,所以我们没法动态创建这个range。有解决办法吗?
对于像这样的固定数字,可以使用元编程递归地构建范围(尽管您可能会达到模板实例化深度限制)。您可以通过 type-erasing 范围来创建真正的动态数字,这样过滤器链就可以通过虚函数调用连接起来。结果很慢,代码很痛苦,但肯定是可以的。
其中一种方案是将每次过滤的结果存储在一个vector
中,这样可以保证每次操作后的范围类型一致,可以重新赋值。
#include <iostream>
#include <ranges>
#include <vector>
auto to_vector(std::ranges::view auto view) {
return std::vector(view.begin(), view.end());
}
int main() {
auto output = to_vector(std::views::iota(2, 100));
for (int i = 2; i < 100; i++) {
output = to_vector(output | std::views::filter(
[i](int num){ return num % i != 0 || num == i; }));
}
std::cout << "The 10th prime is: " << output[9] << "\n";
}
但是,这是低效的,不是使用范围适配器的好用例。所以你可能需要使用更高效的算法来实现这个。
在这种特殊情况下,您可以改为构建过滤谓词:
int main() {
auto output = std::views::iota(2, 100);
std::function<bool(int)> filter_fn = [] (int) { return true; };
for (int i = 2; i < 100; i++)
{
filter_fn = [=] (int num) {
return filter_fn(num) && (num % i != 0 || num == i);
};
}
auto primes = output | std::views::filter(filter_fn);
std::cout << "The 10th prime is: " <<
(primes | std::views::drop(9)).front() << "\n";
}
可以并不意味着应该。这是非常低效的,因为它为谓词创建了一系列间接调用。
我对范围相当陌生,我想知道是否有办法应用动态数量的范围适配器。摸索了一段时间的代码,也搜索了一下,都没有结果。
#include <iostream>
#include <ranges>
int main() {
auto output = std::ranges::views::iota(2, 100);
for (int i = 2; i < 100; i++) {
output = output | std::ranges::views::filter([i](int num){ return num % i != 0 || num == i; });
}
std::cout << "The 10th prime is: " << output[9] << "\n";
}
本质上,我想要这样的东西,但这会产生编译错误(no match for 'operator='
)。好像每申请一个range adapter都需要一个新的类型,所以我们没法动态创建这个range。有解决办法吗?
对于像这样的固定数字,可以使用元编程递归地构建范围(尽管您可能会达到模板实例化深度限制)。您可以通过 type-erasing 范围来创建真正的动态数字,这样过滤器链就可以通过虚函数调用连接起来。结果很慢,代码很痛苦,但肯定是可以的。
其中一种方案是将每次过滤的结果存储在一个vector
中,这样可以保证每次操作后的范围类型一致,可以重新赋值。
#include <iostream>
#include <ranges>
#include <vector>
auto to_vector(std::ranges::view auto view) {
return std::vector(view.begin(), view.end());
}
int main() {
auto output = to_vector(std::views::iota(2, 100));
for (int i = 2; i < 100; i++) {
output = to_vector(output | std::views::filter(
[i](int num){ return num % i != 0 || num == i; }));
}
std::cout << "The 10th prime is: " << output[9] << "\n";
}
但是,这是低效的,不是使用范围适配器的好用例。所以你可能需要使用更高效的算法来实现这个。
在这种特殊情况下,您可以改为构建过滤谓词:
int main() {
auto output = std::views::iota(2, 100);
std::function<bool(int)> filter_fn = [] (int) { return true; };
for (int i = 2; i < 100; i++)
{
filter_fn = [=] (int num) {
return filter_fn(num) && (num % i != 0 || num == i);
};
}
auto primes = output | std::views::filter(filter_fn);
std::cout << "The 10th prime is: " <<
(primes | std::views::drop(9)).front() << "\n";
}
可以并不意味着应该。这是非常低效的,因为它为谓词创建了一系列间接调用。