STL: запись оператора "где" для вектора - PullRequest
8 голосов
/ 08 июня 2010

Мне нужно найти индексы в векторе, основанные на нескольких логических предикатах.

ex:

vector<float> v;
vector<int> idx;

idx=where( bool_func1(v), bool_func2(v), ... );

Как можно объявить функцию **where**, чтобы использоватьнесколько пользовательских булевых функций над вектором?

спасибо, Арман.

Редактировать через неделю

Я сделал несколько сложных решений с шаблонами.Но на самом деле можно использовать уже предопределенный 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;
    }
   }

Ответы [ 5 ]

10 голосов
/ 08 июня 2010

Сделайте ваши функции 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.Даже если вы знаете, что хотите определенный тип композиции сейчас, вы можете сделать ее настолько универсальной, насколько это возможно.Вот как я это сделаю.

Редактировать:

На основании этого комментария: @ Джерри: Например, мне нужно знать: id = где (v <10.0 && v> 1,0);и где-то позже я хотел бы знать: id = где (v вам может быть лучше с boost :: lambda:


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

Это рекурсивно и может быть медленным в некоторых реализациях, но есть один способ сделать это.

3 голосов
/ 08 июня 2010

Вы можете использовать предикатную версию 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; }
};

Давайте соединим все это с конкретным примером. Gizmo ниже - объект, коллекция которого у вас есть. У нас есть 2 предиката find_letter и find_value, по которым мы хотим найти совпадения в нашем основном vector. 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;

}

EDIT:

Основанный на итеративном подходе sbi , вот предикатная версия copy, которая больше соответствует общей парадигме STL и может использоваться с back_insert_iterator для достижения того, что в этом случае требуется , Он даст вам vector объекта, а не итераторов или индексов, поэтому 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;
}
1 голос
/ 08 июня 2010

Это похоже на проблему, которую гораздо проще решить на декларативном языке, таком как Пролог.Я все равно попробовал в 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;
}
0 голосов
/ 08 июня 2010
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>());

Ной Роберт предлагает отличное решение, но я не совсем уверен, как я мог заставить это работать.

0 голосов
/ 08 июня 2010

Я не уверен, какие индексы вы хотите. Это то, что вы пытаетесь достичь:

//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;
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...