在C++20
'sstd::ranges
中,我们可以预期得到1。这可能非常方便,但我在玩它时发现了一个问题。从 Eric Niebler 的手册中,我们可以读到“本质上,将连续元素与二元谓词组合在一起。”。让我们看一个例子。我有一些s ,我想将它分成两个范围 - 代表偶数和奇数。我最初的方法是简单地做:views::group_by
views::group_by
std::vector
int
int main() {
using namespace ranges;
std::vector<int> ints = {3, 9, 12, 10, 7, 5, 1, 4, 8};
for (auto rng : ints | views::group_by(
[](auto lhs, auto rhs) {
const bool leftEven = lhs % 2 == 0;
const bool rightEven = rhs % 2 == 0;
return (leftEven && rightEven) || (!leftEven && !rightEven);
})) {
std::cout << rng << '\n';
}
}
但这行不通。或者,换一种说法,它会起作用,但是对于任何熟悉其他语言(甚至 API)中类似操作的人来说,它会产生意想不到的(我想对某些人来说)结果。这个程序的输出是:
[3,9] [12,10] [7,5,1] [4,8]
偶数和奇数并非全部分组 - 这是因为它们并非都是连续的。3
并且9
配对在一起,因为它们都是ood和contiguous。类似地(除了是even)12
和10
。但是7
, 5
and1
将创建一个单独的组 - 它们不会与3
and分组9
,这不是我想要或期望的。
我们当然可以做的是对partition
向量ints
进行排序,以便偶数和赔率形成两组。问题是......范围内没有。这给我留下了两种选择,它们都没有特别吸引我:views::
partition
1.查看矢量前:std
ranges::partition
std
来电:
ranges::partition(ints, [](auto elem) { return elem % 2 == 0; });
就在我们基于范围for
的循环之前,我们有我们想要的输出:
[8,4,12,10] [7,5,1,9,3]
我不喜欢它,因为它缺乏可组合性——这是ranges
关键因素之一。老实说,我也不想分割向量。我想在两组中打印它的元素 - 偶数和赔率。
2. 使用actions::sort
奇偶比较器对向量进行排序:
int main() {
using namespace ranges;
std::vector<int> ints = {3, 9, 12, 10, 7, 5, 1, 4, 8};
auto evens_first = [](auto lhs, auto rhs) { return lhs % 2 == 0 && rhs % 2 != 0; };
for (auto rng : (ints |= actions::sort(evens_first)) | views::group_by(
[](auto lhs, auto rhs) {
const bool leftEven = lhs % 2 == 0;
const bool rightEven = rhs % 2 == 0;
return (leftEven && rightEven) || (!leftEven && !rightEven);
})) {
std::cout << rng << '\n';
}
}
请注意,|=
运算符周围的括号是必需的,因为否则|
将首先评估范围的组合运算符 ( ),我们最终将使用上面的代码打印向量的排序元素,完全忽略分组 ( ??? )。
这种方法还可以,但仍然不是很好。我更喜欢有一个group_by
可以,例如,取一个值并返回一个键(Java
's 和C#
' 处理分组的方法),或者无论如何考虑整个范围,或者至少有actions::partition
可用的。
旁注:我看到了views::grouping_by
仅使用连续元素的基本原理。这是最有效的方式——无需存储任何东西,无需返回或进一步查看。没关系,有时它是工作的最佳工具。但我认为,对于过去使用过类似 API 的人来说,这违反直觉,会造成混乱。
最后重复这个问题 - 根据我提出的示例和所需的方法,有没有更简洁的方法来做我想做的事?
1我在cppreference上找不到它,但我想我在某处看到了它所在的确认信息。如果我弄错了,请纠正我。