Використання std :: find_if () з функцією порівняння, яка приймає кілька вхідних параметрів

Я намагаюся використати алгоритм std :: find_if() з функцією порівняння, яка приймає кілька вхідних аргументів, але я не впевнений, як його реалізувати в моєму коді. Я шукав використання std :: find_if() на різних сайтах, але всі вони використовували функцію порівняння з одним вхідним аргументом.

using namespace std;

// comparison function
bool range_search(double x, double X1, double X2)
{
    return (x >= X1 && x <= X2) ? true : false;
}

// main   
vector x;

for(int i = 0; i < size; i++){
    x.push_back(...);
};

vector::iterator it = find_if(x.begin(), x.end(), range_search);
int pos_1 = distance(x.begin(), it);
1
Використовувати лямбди або об'єкти функтора?
додано Автор Some programmer dude, джерело
Якщо ви подивитеся на cppreference , можна побачити концепцію функції предиката <�код > UnaryPredicate . Таким чином, функція стандартної бібліотеки буде приймати тільки функцію, яка приймає один аргумент і повертає true/false. Ваша функція приймає 3 аргументи. Отже, вам потрібно обернути її функцією, яка займає лише одну.
додано Автор Paul Rooney, джерело
Чи дозволяється використовувати принаймні стандарт C ++ 11?
додано Автор Bob__, джерело
є константи X1 і X2?
додано Автор P.W, джерело
@ P.W так, X1 і X2 є константами. Але я хочу назвати цю функцію кілька разів. наприклад, X [2] [2] = {{1,2}, {3,4}}; для 1-го випадку: X1 = X [0] [0], X2 = X [0] [1] і для 2-го випадку X1 = X [1] [0], X2 = X [1] [1].
додано Автор Ken, джерело
@Bob__ Так, я можу використовувати стандарт C ++ 11.
додано Автор Ken, джерело

5 Відповіді

Немає версії std :: find_if() , яка приймає предикат з декількома вхідними аргументами. std :: find_if() перебирає заданий діапазон ітераторів, передаючи кожному елементу по одному на предикат. Таким чином, предикат повинен приймати лише 1 вхідний аргумент, не більше, не менше. Тому всі приклади, які ви бачили, використовують 1 аргумент.

Показана функція range_search() просто не сумісна для використання як сам предикат.

У C ++ 11 і пізніших версіях ви можете використовувати лямбда для захоплення додаткових значень, які потрібно передати range_search() , наприклад:

double X1 = ...;
doubke X2 = ...;
auto it = find_if(x.begin(), x.end(),
    [X1, X2](double x){ return range_search(x, X1, X2); }
);

До C ++ 11 замість цього можна використовувати об'єкт функтора:

struct range_search_s
{
    double X1, X2;
    range_search_s(double x1, double x2) : X1(x1), X2(x2) {}
    bool operator()(double x) { return range_search(x, X1, X2); }
};

double X1 = ...;
doubke X2 = ...;
vector::iterator it = find_if(x.begin(), x.end(), range_search_s(X1, X2));
4
додано

find_if requires only one argument to its test function because it has to test each element in the collection in turn. If you want to bind extra variables at call time, use a lambda:

double X1 = 2, X2 = 4;
auto it = find_if(x.begin(), x.end(),
                  [&](double v) { return (v >= X1 && v <= X2); });
3
додано
Thnx, це працює.
додано Автор Ken, джерело

Враховуючи, що std :: find_if приймає тільки одинарний предикат, можна побудувати його, використовуючи функцію вищого порядку з кількома вхідними аргументами, які повертають одинарний лямбда:

#include 
#include 
#include 
#include 
#include 

namespace pred {

template 
constexpr auto is_in_range(const T min_value, const T max_value)
{
    return [min_value, max_value] (T x) { return x >= min_value && x <= max_value; };
}

}

int main()
{
    std::vector x {0.1, -3.0, 1.67, 4.0, 3.14, 1.5, 0.0, 2.0};

    auto it = find_if(x.begin(), x.end(), pred::is_in_range(1.0, 3.0));
    assert(distance(x.begin(), it) == 2  &&  *it == 1.67);  

    auto ranged = pred::is_in_range(0.0, 3.5);

    auto it2 = find_if(x.begin(), x.end(), ranged);   
    assert(distance(x.begin(), it2) == 0  &&  *it2 == 0.1);  

    std::vector y;
    std::copy_if(x.begin(), x.end(), std::back_inserter(y), ranged);
    assert(y.size() == 6);

    std::cout << "So far, so good...\n";
}
1
додано

Використовувати лямбда:

vector:: iterator it = find_if(x.begin(),x.end(),[param1, param2, param3](const double& a, const double& b) { /* use param1-3 here*/ });
0
додано
Не можна використовувати 2 значення параметра між () лямбда, find_if() буде приймати лише один параметр.
додано Автор Remy Lebeau, джерело

Ви можете скористатися std :: bind перетворити функцію з параметрами n в одну з параметрами nx , надавши деякі аргументи як константи:

auto fn = std::bind(&range_search, _1, 0.1, 0.9);

_1 є заповнювачем для першого аргументу, який ми хочемо зберегти змінною. Аргументи 0.1 і 0.9 передаються для параметрів X1 і X2 відповідно.

Оскільки створена функція тепер має лише один параметр, ми можемо використовувати її з std :: find_if() :

auto it = std::find_if(x.begin(), x.end(), fn);

Можна також об'єднати ці два елементи в один виклик:

auto it = std::find_if(x.begin(), x.end(), std::bind(&range_search, _1, 0.1, 0.9));

Live Demo at Coliru

0
додано
IT KPI C/С++ новым годом
IT KPI C/С++ новым годом
747 учасників

Чат обсуждения С/С++. - Вопросы "напишите за меня лабу" - это оффтоп. - Оффтоп, флуд, оскорбления и вбросы здесь не приняты. - За нарушение - предупреждение или mute на неделю. - За спам и рекламу - ban. Все чаты IT KPI: https://t.me/itkpi/1147