简体   繁体   中英

Binary predicate for `std::count_if` is not working

I am currently trying to use a lambda function to std::count_if the sum of two consecutive elements in an array equal to a number. A sample code is given below.

#include <iostream>
#include <vector>
#include <algorithm>

int main()
{
    const int Number = 3;
    std::vector<int> vec = {1,1,2,4,5,6};    

    auto count = std::count_if( vec.begin(), vec.end(),
    [&](int A, int B) -> bool
    {   return A+B == Number;   });

    std::cout << count << '\n';
}

The output should be 1 , since we have one possible case( 1 + 2 ).

However, I could not succeed. Can anybody tell me what do I miss? Here is the error msg:

|234|error: no match for call to '(main()::<lambda(int, int)>) (int&)'|

Problem is that std::count_if uses unary predicate. What compiler tells you: "You gave me a lambda with 2 arguments, I expected lambda with one argument".

I believe what you are looking for is std::adjacent_find . It compares every two adjacent elements of a container (possibly using a binary predicate).

Another possible option is to use std::inner_product . First I'd write a little helper function:

#include <numeric>
#include <functional>
#include <iterator>

template <typename ForwardIterator, typename BinaryPredicate>
auto count_pairs_if(ForwardIterator first, ForwardIterator last, 
                    BinaryPredicate pred)
{
    const auto n = std::distance(first, last);
    if (n < 2) return std::size_t {0};
    return std::inner_product(first, std::next(first, n - 1), std::next(first),
                              std::size_t {0}, std::plus<> {}, pred);
}

template <typename Range, typename BinaryPredicate>
auto count_pairs_if(const Range& values, BinaryPredicate pred)
{
    return count_pairs_if(std::cbegin(values), std::cend(values), pred);
}

Then you can use it like:

auto count = count_pairs_if(vec, [=] (auto lhs, auto rhs) { return lhs + rhs == Number; });

Here's a demo .

As @Yksisarvinen explained, the std::count_if is designed for the unary predicate. Therefore the compiler can not accept the lambda, I passed.

After a while, I have found another solution to this problem. If I provide a templated function, which takes

  1. iterators(ie start and end) of the container (on which I need to do the adjacent element check), and
  2. the binary predicate, which will be used for checking adjacent relationship

that could be a more natural solution, like any other standard algorithm. ( See a live demo online )

template <typename Iterator, typename BinaryPred = std::equal_to<>>
constexpr std::size_t count_adjacent_if(
   Iterator beginIter,
   const Iterator endIter,
   const BinaryPred pred = {})
{
   if (beginIter == endIter) return 0; // nothing to do!
   std::size_t count{};
   for (Iterator nextIter{ beginIter }; ++nextIter != endIter; beginIter = nextIter)
      if (pred(*beginIter, *nextIter))
         ++count;
   return count;
}

and can be called like:

const auto count = ::count_adjacent_if(
   vec.cbegin(), vec.cend(), [number](const int lhs, const int rhs) { return lhs + rhs == number; }
);

Or like @bipil mentioned in the comments , let the predicate remember the previous element. Which is less recommended, since it is a non-generic solution and needs the non-empty container. ( See a live demo online )

int lhs = vec[0];
const auto count = std::count_if(vec.cbegin() + 1, vec.cend(),
   [&](const int rhs) {
   const bool condition = (lhs + rhs == number); // check for the condition
   lhs = rhs; // change the lhs = rhs (i.e. current element = next element)
   return condition; // return the condition
});

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM