假设我有一个向量:
std::vector<Foo> v;
此向量已排序,因此相等的元素彼此相邻。
获取所有表示具有相等元素的范围的迭代器对的最佳方法是什么(使用标准库)?
while (v-is-not-processed) {
iterator b = <begin-of-next-range-of-equal-elements>;
iterator e = <end-of-next-range-of-equal-elements>;
for (iterator i=b; i!=e; ++i) {
// Do something with i
}
}
我想知道如何在上面的代码中获取b
和e
的值。
例如,如果v
包含以下数字:
index 0 1 2 3 4 5 6 7 8 9
value 2 2 2 4 6 6 7 7 7 8
然后我想让b
和e
指向循环中的元素:
iteration b e
1st 0 3
2nd 3 4
3rd 4 6
4th 6 9
5th 9 10
是否可以使用标准库解决此问题?
答案 0 :(得分:25)
您可以使用std::upper_bound
将迭代器设置为“ next”值。由于std::upper_bound
将第一个元素返回的迭代器大于所提供的值,因此,如果您提供当前元素的值,它将为您提供一个迭代器,该迭代器将比当前值的末尾晚一个。那会给你一个像
iterator it = v.begin();
while (it != v.end()) {
iterator b = it;
iterator e = std::upper_bound(it, v.end(), *it);
for (iterator i=b; i!=e; ++i) {
// do something with i
}
it = e; // need this so the loop starts on the next value
}
答案 1 :(得分:24)
这基本上是Range v3的group_by
:group_by(v, std::equal_to{})
。它在C ++ 17标准库中不存在,但是我们可以编写自己的粗略等价物:
template <typename FwdIter, typename BinaryPred, typename ForEach>
void for_each_equal_range(FwdIter first, FwdIter last, BinaryPred is_equal, ForEach f) {
while (first != last) {
auto next_unequal = std::find_if_not(std::next(first), last,
[&] (auto const& element) { return is_equal(*first, element); });
f(first, next_unequal);
first = next_unequal;
}
}
用法:
for_each_equal_range(v.begin(), v.end(), std::equal_to{}, [&] (auto first, auto last) {
for (; first != last; ++first) {
// Do something with each element.
}
});
答案 2 :(得分:17)
您正在寻找std::equal_range
。
返回一个范围,其中包含与 范围 [第一个,最后一个)。
类似以下的方法应该起作用。
mysql -u username -p -h host.domain.edu -P 1234
备注:即使这是一种直观的方法,config/database.php
也会获得其 first 和 second 迭代器(即auto it = v.begin();
while (it != v.end())
{
auto [b, e] = std::equal_range(it, v.end(), *it);
for (; b != e; ++b) { /* do something in the range[b, e) */ }
it = e; // need for the beginning of next std::equal_range
}
和std::equal_range
)借助于std::lower_bound
和std::upper_bound
的使用,这使方法slightly inefficient得到了解决。由于 first 迭代器在OP的情况下很容易访问,因此只需要调用b
进行 second 迭代器(如 @NathanOliver 的答案)。
答案 3 :(得分:8)
如果您的相等值范围较短,那么std::adjacent_find
会很好地工作:
struct
如果您愿意,也可以用λfor (auto it = v.begin(); it != v.end();) {
auto next = std::adjacent_find(it, v.end(), std::not_equal_to<Foo>());
for(; it != next; ++it) {
}
}
代替。
答案 4 :(得分:7)
但是,即使我们什么都不使用e,这种表达方式还是很方便的,但更容易出错。另一种方法(检查值的更改)比较繁琐(因为我们需要专门处理最后一个范围)[...]
取决于您如何理解“特别处理最后一个范围” :
auto begin = v.begin();
// we might need some initialization for whatever on *begin...
for(Iterator i = begin + 1; ; ++i)
{
if(i == v.end() || *i != *begin)
{
// handle range single element of range [begin, ???);
if(i == v.end())
break;
begin = i;
// re-initialize next range
}
}
对最后一个范围没有特殊处理-仅可能需要两次初始化代码...
嵌套循环法:
auto begin = v.begin();
for(;;)
{
// initialize first/next range using *begin
for(Iterator i = begin + 1; ; ++i)
{
if(i == v.end() || *i != *begin)
{
// handle range single element of range [begin, ???);
if(i == v.end())
goto LOOP_EXIT;
begin = i;
break;
}
}
}
LOOP_EXIT:
// go on
// if nothing left to do in function, we might prefer returning over going to...
更优雅?诚然,我自己对此表示怀疑……但这两种方法都避免在相同的范围内两次迭代(首先是找到终点,然后是实际迭代)。如果我们通过以下方式创建自己的库函数:
template <typename Iterator, typename RangeInitializer, typename ElementHandler>
void iterateOverEqualRanges
(
Iterator begin, Iterator end,
RangeInitializer ri, ElementHandler eh
)
{
// the one of the two approaches you like better
// or your own variation of...
}
然后我们可以像这样使用它:
std::vector<...> v;
iterateOverEqualRanges
(
v.begin(), v.end(),
[] (auto begin) { /* ... */ },
[] (auto current) { /* ... */ }
);
现在,终于看起来像e。 G。 std::for_each
,不是吗?
答案 5 :(得分:0)
for(auto b=v.begin(), i=b, e=v.end(); i!=e; b=i) {
// initialise the 'Do something' code for another range
for(; i!=e && *i==*b; ++i) {
// Do something with i
}
}