Двоичный предикат для std :: count_if не работает

В настоящее время я пытаюсь использовать лямбда-функцию для std::count_if суммы двух последовательных элементов в массиве, равной числу. Ниже приведен пример кода.

#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';
}

На выходе должен быть 1, поскольку у нас есть один возможный случай (1 + 2).

Однако мне это не удалось. Кто-нибудь может сказать мне, что мне не хватает? Вот сообщение об ошибке:

|234|error: no match for call to '(main()::<lambda(int, int)>) (int&)'|
std::count_if ожидает функцию с одним параметром.
HolyBlackCat 07.04.2018 01:18
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
2
1
374
3
Перейти к ответу Данный вопрос помечен как решенный

Ответы 3

Проблема в том, что std :: count_if использует унарный предикат. Что компилятор сообщает вам: «Вы дали мне лямбду с двумя аргументами, я ожидал лямбда с одним аргументом».

Я считаю, что вы ищете std :: neighbour_find. Он сравнивает каждые два соседних элемента контейнера (возможно, используя двоичный предикат).

Хотя соседний_find находит только первую пару, но не считает их.

HolyBlackCat 07.04.2018 01:19

Хм, верно. Я не уверен, есть ли в <algorithm> какая-либо функция, которая могла бы принимать двоичный предикат для подсчета элементов. Может потребоваться внешний цикл для подсчета найденных вхождений.

Yksisarvinen 07.04.2018 01:23

Вероятно, этого можно достичь с помощью count_if: 1) вы определяете собственный итератор, который разыменовывается на пару соседних значений; 2) ваш предикат более глубокий, т.е. он принимает эту пару.

bipll 07.04.2018 01:37

@Yksisarvinen HolyBlackCat: вы оба правы. Я пробовал с std::adjacent_find. Он вернет только первый случай. Для расширения мне нужно использовать петлю на основе iterator и передать диапазон в std::adjacent(). В любом случае, спасибо за поддержку.

JeJo 07.04.2018 01:39

Или, может быть, даже с предикатом с отслеживанием состояния, который запоминает предыдущее значение.

bipll 07.04.2018 01:40

Вы можете написать шаблонную функцию adjacent_count, которая была бы очень похожа на std::adjacent_find, просто запуская ее в цикле и возвращая количество найденных элементов.

Yksisarvinen 07.04.2018 01:43

Другой возможный вариант - использовать std::inner_product. Сначала я бы написал небольшую вспомогательную функцию:

#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);
}

Тогда вы можете использовать это как:

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

Вот демонстрация.

Ответ принят как подходящий

Как объяснил @Yksisarvinen, std::count_if разработан для унарного предиката. Поэтому компилятор не может принять лямбду, которую я пропустил.

Спустя время я нашел другое решение этой проблемы. Если я предоставлю шаблонную функцию, которая принимает

  1. итераторы (т.е. начало и конец) контейнера (на котором мне нужно выполнить проверку соседнего элемента) и
  2. бинарный предикат, который будет использоваться для проверки соседнего отношения

это могло бы быть более естественным решением, как и любой другой стандартный алгоритм. (Посмотреть живую демонстрацию онлайн)

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;
}

и может называться так:

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

Или, как @bipil упоминается в комментариях, пусть предикат запомнит предыдущий элемент. Это менее рекомендуется, поскольку это не универсальное решение и требует непустого контейнера. (Посмотреть живую демонстрацию онлайн)

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
});

Другие вопросы по теме