Могу ли я использовать диапазоны 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. Все предикаты в диапазонах должны сохранять равенство - одинаковые входные данные, одинаковые выходные данные. Здесь это не так - если мы вызовем предикат дважды для одного элемента, мы получим другой результат. Так что это неопределенное поведение.

Другие вопросы по тегам