Я должен найти индексы в векторе на основе нескольких булевых предикатов.
исключая:
vector<float> v;
vector<int> idx;
idx=where( bool_func1(v), bool_func2(v), ... );
Что состоит в том, чтобы объявить путь **where**
функция, для использования нескольких определяемых пользователем булевых функций по вектору?
спасибо Arman.
Редактирование после одной недели
Я сделал некоторые сложные решения с шаблонами. Но в действительности можно уже использовать предопределенный valarray
для моих задач. Вот фрагмент кода, возможно, можно найти это полезным:
double dr=Rc/(double)Nbins, r;
sigma.resize(Nbins);
rr=sigma;
valarray<double> vz(&data.vz[0], data.vz.size());
double mvel=vz.sum()/(double)vz.size();
for(size_t i=0l;i<Nbins;i++)
{
r=dr*i;
valarray<bool> ids = (dist < r+dr) && (dist > r);//The magic valarray<bool>
if(ids.max())
{
valarray<double> d=vz[ids];//we can use indirect operation.
d-=mvel;
d=pow(d,2.0);
sigma[i]= sqrt(d.sum()/(double)d.size());
rr[i]=r;
cout<<i<<") "<<r<<" "<<sigma[i]<<endl;
}
}
Сделайте ваши функции bool_xxx на самом деле функторами определенного типа (достаточно диспетчеризации тегов). Затем переопределите || и && для них, чтобы эти операторы возвращали bool_and или bool_or. Затем вы можете использовать свои предикаты bool_ следующим образом:
std::find_if(vect.begin(), vect.end(), bool_x1() || bool_x2() && (bool_x3() || bool_x4() && bool_x5()));
Если вы испытываете искушение написать функцию «где», то вы, очевидно, захотите сделать это более одного раза с другим набором функций bool_xxx. Даже если вы знаете, что хотите получить определенный тип композиции сейчас, вы можете сделать ее как можно более универсальной. Вот как бы я это сделал.
Изменить:
На основании этого комментария: @Jerry: Например, мне нужно знать: id = where (v <10.0 && v> 1.0); и где-то позже я хотел бы знать: id = where (v
namespace l = boost::lambda;
std::find_if(vect.begin(), vect.end(), l::_1 < 10.0 && l::_1 > 1.0);
std::find_if(vect.begin(), vect.end(), l::_1 < l::bind(fun, l::_1));
Или, если вы ненавидите лямбду или вам не разрешено используйте его ... или просто хотите немного более приятный синтаксис (но неспособность использовать функции напрямую), тогда просто создайте свой собственный тип-заполнитель и переопределите его, чтобы он возвращал функторы bool_xxx для операторов <,> и т.д ...
Edit2: Вот непроверенный пример, в котором он возвращает вектор итераторов для всех совпадающих объектов:
template < typename ForwardIter, typename Predicate >
std::vector<ForwardIter> where(ForwardIter beg, ForwardIter end, Predicate pred)
{
ForwardIter fit = std::find_if(beg,end,pred);
if (fit == end) return std::vector<ForwardIter>();
ForwardIter nit = fit; ++nit;
std::vector<ForwardIter> collection = where(nit,end,pred);
collection.push_front(fit);
return collection;
}
Это рекурсивно и может быть медленным в некоторых реализациях, но есть один способ сделать это.
Это похоже на проблему, которую гораздо проще решить на декларативном языке, таком как Prolog. Я все равно попробовал на C ++:
typedef float type;
typedef bool (*check)(type);
std::vector<int> where(const std::vector<type>& vec,
const std::vector<check>& checks)
{
std::vector<int> ret;
for (int i = 0; i < vec.size(); i++)
{
bool allGood = true;
for (int j = 0; j < checks.size(); j++)
{
if (!checks[j](vec[i]))
{
allGood = false;
break;
}
}
if (allGood)
ret.push_back(i);
}
return ret;
}
Я не уверен, какие индексы вам нужны. Вот что вы пытаетесь достичь:
//Function pointer declaration
typedef bool (*Predicate)(const std::vector<float>& v);
//Predicates
bool bool_func1(const std::vector<float>& v)
{
//Implement
return true;
}
bool bool_func2(const std::vector<float>& v)
{
//Implement
return true;
}
std::vector<int> where_func(const std::vector<float>& v,
const std::vector<Predicate>& preds)
{
std::vector<int> idxs;
std::vector<Predicate>::const_iterator iter = preds.begin();
std::vector<Predicate>::const_iterator eiter = preds.end();
for(; iter != eiter; ++iter)
{
if((*iter)(v))
{
idxs.push_back(eiter - iter);
}
}
return idxs;
}
template<typename Vector, typename T> std::vector<int> where(const std::vector<Vector>& vec, T t) {
std::vector<int> results;
for(int i = 0; i < vec.size(); i++) {
if (t(vec[i])
results.push_back(i)
}
return results;
}
Перегрузка для дополнительных аргументов объекта функции по вашему желанию. Используйте:
template<typename T> struct AlwaysAccept {
bool operator()(const T& t) { return true; }
};
std::vector<float> floats;
// insert values into floats here
std::vector<int> results = where(floats, AlwaysAccept<float>());
Решение Ноя Роберта хорошее, но я не совсем уверен, как я мог бы заставить это работать.
Вы можете использовать предикативную версию transform
, если бы она была.Нет ни одного, но его очень легко написать:
template<class InputIterator, class OutputIterator, class UnaryFunction, class Predicate>
OutputIterator transform_if(InputIterator first,
InputIterator last,
OutputIterator result,
UnaryFunction f,
Predicate pred)
{
for (; first != last; ++first)
{
if( pred(*first) )
*result++ = f(*first);
}
return result;
}
Тогда вам понадобится способ составить композицию из нескольких предикатов, чтобы вы могли выразить что-то вроде find_if (begin, end, condition1 && condition2)
. Это, опять же, легко написать:
template<typename LHS, typename RHS> struct binary_composite : public std::unary_function<Gizmo, bool>
{
binary_composite(const LHS& lhs, const RHS& rhs) : lhs_(&lhs), rhs_(&rhs) {};
bool operator()(const Gizmo& g) const
{
return lhs_->operator()(g) && rhs_->operator()(g);
}
private:
const LHS* lhs_;
const RHS* rhs_;
};
Наконец, вам понадобится приспособление, которое transform_if
использует для преобразования ссылки на объект в указатель на объект. Сюрприз, удивление, легко написать ...
template<typename Obj> struct get_ptr : public std::unary_function<Obj, Obj*>
{
Obj* operator()(Obj& rhs) const { return &rhs; }
};
Давайте свяжем все это на конкретном примере. Гизмо
ниже - это объект, который у вас есть. У нас есть 2 предиката find_letter
и find_value
, которым мы хотим найти совпадения в нашем основном векторе
. transform_if
- это предикативная версия transform
, get_ptr
преобразует ссылку на объект в указатель, а binary_composite
объединяет два составных элемента.
#include <cstdlib>
#include <iostream>
#include <algorithm>
#include <string>
#include <functional>
#include <vector>
using namespace std;
struct Gizmo
{
string name_;
int value_;
};
struct find_letter : public std::unary_function<Gizmo, bool>
{
find_letter(char c) : c_(c) {}
bool operator()(const Gizmo& rhs) const { return rhs.name_[0] == c_; }
private:
char c_;
};
struct find_value : public std::unary_function<Gizmo, int>
{
find_value(int v) : v_(v) {};
bool operator()(const Gizmo& rhs) const { return rhs.value_ == v_; }
private:
int v_;
};
template<typename LHS, typename RHS> struct binary_composite : public std::unary_function<Gizmo, bool>
{
binary_composite(const LHS& lhs, const RHS& rhs) : lhs_(&lhs), rhs_(&rhs) {};
bool operator()(const Gizmo& g) const
{
return lhs_->operator()(g) && rhs_->operator()(g);
}
private:
const LHS* lhs_;
const RHS* rhs_;
};
template<typename LHS, typename RHS> binary_composite<LHS,RHS> make_binary_composite(const LHS& lhs, const RHS& rhs)
{
return binary_composite<LHS, RHS>(lhs, rhs);
}
template<class InputIterator, class OutputIterator, class UnaryFunction, class Predicate>
OutputIterator transform_if(InputIterator first,
InputIterator last,
OutputIterator result,
UnaryFunction f,
Predicate pred)
{
for (; first != last; ++first)
{
if( pred(*first) )
*result++ = f(*first);
}
return result;
}
template<typename Obj> struct get_ptr : public std::unary_function<Obj, Obj*>
{
Obj* operator()(Obj& rhs) const { return &rhs; }
};
int main()
{
typedef vector<Gizmo> Gizmos;
Gizmos gizmos;
// ... fill the gizmo vector
typedef vector<Gizmo*> Found;
Found found;
transform_if(gizmos.begin(), gizmos.end(), back_inserter(found), get_ptr<Gizmo>(), binary_composite<find_value,find_letter>(find_value(42), find_letter('a')));
return 0;
}
На основе итеративного подхода sbi , вот предикативная версия copy
, которая больше соответствует общей парадигме STL и может использоваться с back_insert_iterator
, чтобы выполнить то, что требуется в этом случае. Он даст вам вектор
объекта, а не итераторы или индексы, поэтому transform_if
, который я опубликовал выше, все же лучше для этого использования, чем copy_if
. Но вот оно ...
template<class InputIterator, class OutputIterator, class Predicate>
OutputIterator copy_if(InputIterator first,
InputIterator last,
OutputIterator result,
Predicate pred)
{
for (; first != last; ++first)
{
if( pred(*first) )
*result++ = *first;
}
return result;
}