В настоящее время я пытаюсь использовать лямбда-функцию для 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 использует унарный предикат. Что компилятор сообщает вам: «Вы дали мне лямбду с двумя аргументами, я ожидал лямбда с одним аргументом».
Я считаю, что вы ищете std :: neighbour_find. Он сравнивает каждые два соседних элемента контейнера (возможно, используя двоичный предикат).
Хотя соседний_find находит только первую пару, но не считает их.
Хм, верно. Я не уверен, есть ли в <algorithm> какая-либо функция, которая могла бы принимать двоичный предикат для подсчета элементов. Может потребоваться внешний цикл для подсчета найденных вхождений.
Вероятно, этого можно достичь с помощью count_if: 1) вы определяете собственный итератор, который разыменовывается на пару соседних значений; 2) ваш предикат более глубокий, т.е. он принимает эту пару.
@Yksisarvinen HolyBlackCat: вы оба правы. Я пробовал с std::adjacent_find. Он вернет только первый случай. Для расширения мне нужно использовать петлю на основе iterator и передать диапазон в std::adjacent(). В любом случае, спасибо за поддержку.
Или, может быть, даже с предикатом с отслеживанием состояния, который запоминает предыдущее значение.
Вы можете написать шаблонную функцию adjacent_count, которая была бы очень похожа на std::adjacent_find, просто запуская ее в цикле и возвращая количество найденных элементов.
Другой возможный вариант - использовать 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 разработан для унарного предиката. Поэтому компилятор не может принять лямбду, которую я пропустил.
Спустя время я нашел другое решение этой проблемы. Если я предоставлю шаблонную функцию, которая принимает
это могло бы быть более естественным решением, как и любой другой стандартный алгоритм. (Посмотреть живую демонстрацию онлайн)
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
});
std::count_ifожидает функцию с одним параметром.