Иногда возникает задача подсчета заданных элементов в контейнере.
Например, пусть имеется массив целых чисел
int a[] = { 1, 2, 3, 1, 2, 2 };
и требуется подсчитать, сколько раз в этом массиве присутствуют значения 1 и 2.
Как это можно сделать для произвольных контейнеров и заданных значений, написав соответствующую функцию?
На ум помимо обычных циклов сразу же приходит стандартный алгоритм
std::count или
std::count_if.
Один из подходов может выглядеть следующим образом
#include <iostream>
#include <algorithm>
#include <iterator>
#include <initializer_list>
template <typename R, typename T>
auto matches( const R &range, std::initializer_list<T> lst )
{
return std::count_if( std::begin( range ), std::end( range ),
[&]( auto x )
{
return std::binary_search( std::begin( lst ), std::end( lst ), x );
} );
}
int main()
{
int a[] = { 1, 2, 3, 1, 2, 2 };
std::cout << matches( a, { 1, 2 } ) << std::endl;
}
Вывод программы на консоль
5
В этой реализации функции помимо стандартного алгоритма
std::count_if используется алгоритм
std::binary_search. Чтобы его можно было использовать в данном контексте, заданные значения должны быть отсортированы, чтобы образовать отсортированный список инициализации.
Другой подход - это использовать так называемые "сворачиваемые выражения" (
fold expressions), введенные в стандарте
C++ 17.
Вот как будет выглядеть соответствующая реализация функции.
#include <iostream>
#include <algorithm>
#include <iterator>
template <typename R, typename ...Ts>
auto matches( const R &range, Ts... ts )
{
return ( std::count( std::begin( range ), std::end( range ), ts ) + ... + 0 );
}
int main()
{
int a[] = { 1, 2, 3, 1, 2, 2 };
std::cout << matches( a, 1, 2 ) << std::endl;
}
Здесь уже не важно отсортированы ли заданные значения или нет, так как алгоритм
std::count вызывается отдельно для каждого заданного значения.
Если у вас есть свои идеи, как выполнить эту задачу, то можете разместить сообщения к теме со своими предложениями.