c++ algorithm c++11 counting c++-standard-library

c++ - ¿Qué algoritmo STL puede determinar si exactamente un elemento en un contenedor satisface un predicado?



algorithm c++11 (4)

Usando std::not_fn para negar un predicado

Como el núcleo del algoritmo de esta pregunta (como se ha cubierto elegantemente al combinar none_of y none_of en la respuesta aceptada ), con cortocircuito en caso de falla, es escanear un contenedor en busca de un predicado único y cuando se encuentre, continúe escaneando el resto del contenedor para la negación del predicado, mencionaré también el negador std::not_fn introducido en C ++ 17, reemplazando las construcciones std::not1 y std::not2 std::not1 menos útiles.

Podemos usar std::not_fn para implementar la misma lógica de predicado que la respuesta aceptada ( std::find_if seguido condicionalmente por std::none_of ), pero con una semántica algo diferente, reemplazando el último paso ( std::none_of ) con none_of sobre la negación del predicado unario usado en el primer paso ( std::find_if ). P.ej:

// C++17 #include <algorithm> // std::find_if #include <functional> // std::not_fn #include <ios> // std::boolalpha #include <iostream> #include <iterator> // std::next #include <vector> template <class InputIt, class UnaryPredicate> constexpr bool one_of(InputIt first, InputIt last, UnaryPredicate p) { auto it = std::find_if(first, last, p); return (it != last) && std::all_of(std::next(it), last, std::not_fn(p)); } int main() { const std::vector<int> v{1, 3, 5, 6, 7}; std::cout << std::boolalpha << "Exactly one even number : " << one_of(v.begin(), v.end(), [](const int n) { return n % 2 == 0; }); // Exactly one even number : true }

Un enfoque de paquete de parámetros para contenedores de tamaño estático

Como ya he limitado esta respuesta a C ++ 14 (y más allá), incluiré un enfoque alternativo para contenedores de tamaño estático (aquí se aplica para std::array , específicamente), haciendo uso de std::index_sequence combinado con paquete de parámetros de expansión:

#include <array> #include <ios> // std::boolalpha #include <iostream> #include <utility> // std::(make_)index_sequence namespace detail { template <typename Array, typename UnaryPredicate, std::size_t... I> bool one_of_impl(const Array& arr, const UnaryPredicate& p, std::index_sequence<I...>) { bool found = false; auto keep_searching = [&](const int n){ const bool p_res = found != p(n); found = found || p_res; return !found || p_res; }; return (keep_searching(arr[I]) && ...) && found; } } // namespace detail template <typename T, typename UnaryPredicate, std::size_t N, typename Indices = std::make_index_sequence<N>> auto one_of(const std::array<T, N>& arr, const UnaryPredicate& p) { return detail::one_of_impl(arr, p, Indices{}); } int main() { const std::array<int, 5> a{1, 3, 5, 6, 7}; std::cout << std::boolalpha << "Exactly one even number : " << one_of(a, [](const int n) { return n % 2 == 0; }); // Exactly one even number : true }

Esto también provocará un cortocircuito si se produce un fallo temprano ("se encontraron más de uno"), pero contendrá algunas comparaciones booleanas más simples que en el enfoque anterior.

Necesito un algoritmo STL que tome un predicado y una colección y devuelva true si uno y solo un miembro de la colección satisface el predicado; de lo contrario, devuelve false .

¿Cómo haría esto usando algoritmos STL?

Por ejemplo, para reemplazar lo siguiente con el código de algoritmo STL para expresar el mismo valor de retorno.

int count = 0; for( auto itr = c.begin(); itr != c.end(); ++itr ) { if ( predicate( *itr ) ) { if ( ++count > 1 ) { break; } } } return 1 == count;


A partir de la respuesta de @ formerlyknownas_463035818, esto puede generalizarse para ver si un contenedor tiene exactamente n elementos que satisfacen un predicado. ¿Por qué? Porque esto es C ++ y no estamos satisfechos hasta que podamos leer el correo electrónico en tiempo de compilación.

template<typename Iterator, typename Predicate> bool has_exactly_n(Iterator begin, Iterator end, size_t count, Predicate predicate) { if(count == 0) { return std::none_of(begin, end, predicate); } else { auto iter = std::find_if(begin, end, predicate); return (iter != end) && has_exactly_n(std::next(iter), end, count - 1, predicate); } }


Dos cosas vienen a mi mente:

std::count_if y luego compara el resultado con 1 .

Para evitar atravesar todo el contenedor en caso de que, por ejemplo, los dos primeros elementos ya coincidan con el predicado, usaría dos llamadas buscando elementos coincidentes. Algo a lo largo de la linea de

auto it = std::find_if(begin,end,predicate); if (it == end) return false; ++it; return std::none_of(it,end,predicate);

O si lo prefieres más compacto:

auto it = std::find_if(begin,end,predicate); return (it != end) && std::none_of(std::next(it),end,predicate);

Los créditos se asignan a Remy Lebeau para la compactación, Deduplicator para el desguace y Blastfurnance para darse cuenta de que tampoco podemos utilizar none_of los algoritmos none_of .


Puede usar std::count_if para contar y devolver si es uno.

Por ejemplo:

#include <iostream> #include <algorithm> // std::count_if #include <vector> // std::vector #include <ios> // std::boolalpha template<class Iterator, class UnaryPredicate> constexpr bool is_count_one(Iterator begin, const Iterator end, UnaryPredicate pred) { return std::count_if(begin, end, pred) == 1; } int main() { std::vector<int> vec{ 2, 4, 3 }; // true: if only one Odd element present in the container std::cout << std::boolalpha << is_count_one(vec.cbegin(), vec.cend(), [](const int ele) constexpr noexcept -> bool { return ele & 1; }); return 0; }

Actualización : Sin embargo, std::count_if cuenta con un elemento completo en el contenedor, lo que no es tan bueno como el algoritmo dado en la pregunta. El mejor enfoque utilizando las colecciones de algoritmos estándar se ha mencionado en la respuesta de @formerlyknownas_463035818 .

Dicho esto, el enfoque de OP también es bueno como el mejor enfoque estándar mencionado anteriormente, donde ocurre un cortocircuito cuando el count llega a 2 . Si alguien está interesado en una función de plantilla de algoritmo no estándar para el enfoque de OP, aquí está.

#include <iostream> #include <vector> // std::vector #include <ios> // std::boolalpha #include <iterator> // std::iterator_traits template<class Iterator, class UnaryPredicate> bool is_count_one(Iterator begin, const Iterator end, UnaryPredicate pred) { typename std::iterator_traits<Iterator>::difference_type count{ 0 }; for (; begin != end; ++begin) { if (pred(*begin) && ++count > 1) return false; } return count == 1; } int main() { std::vector<int> vec{ 2, 3, 4, 2 }; // true: if only one Odd element present in the container std::cout << std::boolalpha << is_count_one(vec.cbegin(), vec.cend(), [](const int ele) constexpr noexcept -> bool { return ele & 1; }); return 0; }

Ahora que se puede generalizar , al proporcionar un parámetro más, el número de N elemento (s) tiene / tiene que encontrarse en el contenedor.

template<typename Iterator> using diff_type = typename std::iterator_traits<Iterator>::difference_type; template<class Iterator, class UnaryPredicate> bool has_exactly_n(Iterator begin, const Iterator end, UnaryPredicate pred, diff_type<Iterator> N = 1) { diff_type<Iterator> count{ 0 }; for (; begin != end; ++begin) { if (pred(*begin) && ++count > N) return false; } return count == N; }