Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Determine whether a predicate holds for none, some or all elements of a range

Tags:

c++

algorithm

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:

  • The predicate holds on all elements and on no elements: the range is empty;
  • The predicate holds on all elements (and the range is not empty);
  • The predicate holds on no elements (and the range is not empty);
  • The predicate holds on some but not all elements.

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.

like image 584
ecatmur Avatar asked Sep 26 '14 10:09

ecatmur


2 Answers

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;
  }
}
like image 74
Csq Avatar answered Nov 04 '22 00:11

Csq


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;
}
like image 34
Kolja Avatar answered Nov 03 '22 23:11

Kolja