C++ 范围是否支持视图中的投影?
Do C++ ranges support projections in views?
我知道范围内的算法(例如 sort
)支持投影,但在我看来,没有办法为视图获得该功能...
我说得对吗?
例如考虑 following working code:
#include <algorithm>
#include <ranges>
#include <vector>
#include <iostream>
enum Color {
Red,
Green,
Blue
};
struct Cat {
int age;
Color color;
};
int main() {
std::vector<Cat> cats{{.age = 10,.color=Color::Red}, {.age = 20,.color=Color::Blue}, {.age = 30,.color=Color::Green}};
auto is_red = [](const auto& cat) {return cat.color == Color::Red;};
for (const auto& cat: cats | std::views::filter(is_red)) {
std::cout << cat.age << std::endl;
}
}
有没有办法删除 lambda 并执行类似的操作:
for (const auto& cat: cats | std::views::filter(&Cat::color, Color::Red) {
注意:我的问题是成员变量投影,但显然在实际代码中也需要成员函数调用。
Do C++ ranges support projections in views?
否(尽管 range-v3 做到了†)。
Is there a way to remove the lambda and do something like:
std::views::filter(&Cat::color, Color::Red)
无论如何,这并不是真正适用于投影的方式。应该是:
filter([](Color c){ return c == Color::Red; }, &Cat::color)
如果你有一个 returns 谓词的 equals
,你可以减少它:
filter(equals(Color::Red), &Cat::color)
但是在算法中添加预测是没有必要的。您始终可以手动提供投影。使用 Boost.Hof 适当命名的 proj
函数适配器,它满足 proj(p, f)(xs...) == f(p(xs)...)
(即我们在将每个参数传递给 f
之前对每个参数应用 p
) :
filter(proj(&Cat::color, [](Color c){ return c == Color::Red; }))
或者,更短的:
filter(proj(&Cat::color, _ == Color::Red))
Demo.
†即使在 range-v3 implementation 中,也不是 remove_if_view
明确支持投影。是手动进行投影的重载为你组成谓词compose(pred, proj)
。在 range-v3 中,compose(f, g)(xs...)
可以表示 f(g(xs...))
或 f(g(xs)...)
,具体取决于 g
的调用方式。所以在这种情况下,它是一个投影而不是函数组合。在 Boost.Hof 中,这两种情况有不同的 compose
和 proj
适配器。
其他答案是正确的,因为我对投影的使用是错误的,我想要一个投影并且也等效于(假设 C++ 中的 std::filter
被命名为 std::filter_if
)虚构的 std::filter
使用==
用于比较,就像我们在 STL 中有 std::count
/std::count_if
std::find
/std::find_if
对。
如果有人需要这样的东西,这似乎可行,但它可能会很慢,因为在 lambdas 中完美转发是 tricky 要做的,所以我没有打扰。
(我使用 keep 因为它对我来说更好听,但它也可以命名为 filter_memb
)。
template<typename Member, typename Val>
auto keep_memb(const Member& memb, const Val& val){
return std::views::filter([=](auto& object){return std::invoke(memb, object) == val; } );
}
我知道范围内的算法(例如 sort
)支持投影,但在我看来,没有办法为视图获得该功能...
我说得对吗?
例如考虑 following working code:
#include <algorithm>
#include <ranges>
#include <vector>
#include <iostream>
enum Color {
Red,
Green,
Blue
};
struct Cat {
int age;
Color color;
};
int main() {
std::vector<Cat> cats{{.age = 10,.color=Color::Red}, {.age = 20,.color=Color::Blue}, {.age = 30,.color=Color::Green}};
auto is_red = [](const auto& cat) {return cat.color == Color::Red;};
for (const auto& cat: cats | std::views::filter(is_red)) {
std::cout << cat.age << std::endl;
}
}
有没有办法删除 lambda 并执行类似的操作:
for (const auto& cat: cats | std::views::filter(&Cat::color, Color::Red) {
注意:我的问题是成员变量投影,但显然在实际代码中也需要成员函数调用。
Do C++ ranges support projections in views?
否(尽管 range-v3 做到了†)。
Is there a way to remove the lambda and do something like:
std::views::filter(&Cat::color, Color::Red)
无论如何,这并不是真正适用于投影的方式。应该是:
filter([](Color c){ return c == Color::Red; }, &Cat::color)
如果你有一个 returns 谓词的 equals
,你可以减少它:
filter(equals(Color::Red), &Cat::color)
但是在算法中添加预测是没有必要的。您始终可以手动提供投影。使用 Boost.Hof 适当命名的 proj
函数适配器,它满足 proj(p, f)(xs...) == f(p(xs)...)
(即我们在将每个参数传递给 f
之前对每个参数应用 p
) :
filter(proj(&Cat::color, [](Color c){ return c == Color::Red; }))
或者,更短的:
filter(proj(&Cat::color, _ == Color::Red))
Demo.
†即使在 range-v3 implementation 中,也不是 remove_if_view
明确支持投影。是手动进行投影的重载为你组成谓词compose(pred, proj)
。在 range-v3 中,compose(f, g)(xs...)
可以表示 f(g(xs...))
或 f(g(xs)...)
,具体取决于 g
的调用方式。所以在这种情况下,它是一个投影而不是函数组合。在 Boost.Hof 中,这两种情况有不同的 compose
和 proj
适配器。
其他答案是正确的,因为我对投影的使用是错误的,我想要一个投影并且也等效于(假设 C++ 中的 std::filter
被命名为 std::filter_if
)虚构的 std::filter
使用==
用于比较,就像我们在 STL 中有 std::count
/std::count_if
std::find
/std::find_if
对。
如果有人需要这样的东西,这似乎可行,但它可能会很慢,因为在 lambdas 中完美转发是 tricky 要做的,所以我没有打扰。
(我使用 keep 因为它对我来说更好听,但它也可以命名为 filter_memb
)。
template<typename Member, typename Val>
auto keep_memb(const Member& memb, const Val& val){
return std::views::filter([=](auto& object){return std::invoke(memb, object) == val; } );
}