Могу ли я использовать диапазоны C++20 для разрыва, когда количество совпадений превышает некоторый порог?
Рассмотрим следующий код предварительного диапазона:
std::vector<int> v(1000*1000);
bool count_gt_5_v1(int val){
return std::count(v.begin(), v.end(), val)>5;
}
Он выглядит лучше, чем необработанный цикл, но может быть очень неэффективным, если val очень часто встречается в v.
Есть ли способ использовать диапазоны C++20, чтобы итерация останавливалась после того, как я встречу val 6 раз. Другими словами, я ищу способ ввести перерыв, когда мое условие будет выполнено. У меня есть эта мерзость, которая вроде бы работает, но намного уродливее, чем необработанный цикл for.
bool count_gt_5_v2(int val){
int cnt=0;
auto span = std::ranges::views::take_while(v,[&cnt, &val]
(const auto elem)
{
cnt+=elem==val;
return cnt<6;
});
std::ranges::distance(span);
return cnt==6;
}
Ссылка на полный код: https://godbolt.org/z/86djdK
1 ответ
Вы могли сделать это:
auto matches = v | rv::filter([=](int i){ return i == val; })
| rv::take(6);
return ranges::distance(matches) == 6;
Или лучше:
auto matches = v | rv::filter([=](int i){ return i == val; });
return not ranges::empty(matches | rv::drop(5));
Эта попытка:
std::ranges::views::take_while(v, [&cnt, &val](const auto elem){
cnt+=elem==val;
return cnt<6;
});
не соответствует требованиям take_while
. Все предикаты в диапазонах должны сохранять равенство - одинаковые входные данные, одинаковые выходные данные. Здесь это не так - если мы вызовем предикат дважды для одного элемента, мы получим другой результат. Так что это неопределенное поведение.