Reputation: 157374
Since C++11, in the algorithms library we have all_of
, any_of
and none_of
to determine whether a predicate holds on all, any or none of the elements of a range. A single call to one of these algorithms returns 1 bit of information, whereas for a particular range and predicate there are 4 possibilities:
Is there a concise and efficient way to find this information? Calling all_of
followed by none_of
is a possibility, but (a) fails to work on single-pass ranges, and (b) evaluates the predicate exactly once more than necessary. Boost would be acceptable.
Upvotes: 8
Views: 683
Reputation: 137622
You can do this with the standard library functions you describe. Test both whether any elements are true, and whether any elements are false, then combine the results according to this table:
any true | none true
======================
any false | mixed | all false |
none false | all true | empty |
Upvotes: 0
Reputation: 157374
This is my preferred algorithm (enum from @Angew):
enum class Result {
empty, all, none, some
};
template<class InputIt, class UnaryPredicate>
Result examine_range(InputIt first, InputIt last, UnaryPredicate p)
{
bool all = true, none = true;
for (; first != last && (all || none); ++first)
(p(*first) ? none : all) = false;
return all ? (none ? Result::empty : Result::all)
: (none ? Result::none : Result::some);
}
Upvotes: 0
Reputation: 1247
Am I understanding this question wrongly, or is this something you could do via std::accumulate
?
using eana = std::tuple<bool, bool, bool, bool>;
template <typename T, typename FwdIt, typename Pred>
auto empty_all_none_any(FwdIt begin, FwdIt end, Pred predicate) -> eana {
auto result = eana{begin == end, begin != end, begin != end, false};
result = std::accumulate(begin, end, result, [&](eana& res, T& val) {
if (predicate(val)) {
std::get<2>(res) = false;
std::get<3>(res) = true;
}
else {
std::get<1>(res) = false;
}
return res;
});
return result;
}
Upvotes: 1
Reputation: 5855
You can eliminate both (a) the (b) problems if you examine the first element manually and choose between all_of
or none_of
depending on the result.
Code (borrowed the enum
from @Angew):
enum class Result {
empty, all, none, some
};
template <class FwIt, class Pred>
Result examine_range(FwIt from, FwIt to, Pred pred)
{
if (from == to) return Result::empty;
if (pred(*from)) {
++from;
return all_of(from,to,pred) ? Result::all : Result::some;
} else {
++from;
return none_of(from,to,pred) ? Result::none : Result::some;
}
}
Upvotes: 10