STL:写“哪里”向量的运算符

发布于 2024-09-05 04:30:53 字数 986 浏览 3 评论 0原文

我需要根据几个布尔谓词找到向量中的索引。

例如:

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

I need to find the indexes in the vector based on several boolean predicates.

ex:

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

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

What is the way to declare **where** function, in order to use the several user defined boolean functions over the vector?

thanks
Arman.

Edit after one week

I did some complex solutions with templates. But in reality one can use already predefined valarray for my tasks. Here is the code snippet maybe one can find it useful:

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

于我来说 2024-09-12 04:30:53

让你的 bool_xxx 函数实际上是特定类型的函子(标签调度就足够了)。然后覆盖 ||和&&对于他们来说,这些运算符返回 bool_and 或 bool_or。然后您可以像这样使用 bool_ 谓词:


std::find_if(vect.begin(), vect.end(), bool_x1() || bool_x2() && (bool_x3() || bool_x4() && bool_x5()));

如果您想编写一个“where”函数,那么您显然希望使用一组不同的 bool_xxx 函数多次执行此操作。即使你知道你现在想要某种类型的作品,你也可以让它尽可能通用。我就是这样做的。

编辑:

基于此评论:@Jerry:例如我需要知道: id=where(v < 10.0 && v>1.0);稍后我想知道: id=where(v < fun(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));

或者,如果你讨厌 lambda 或不允许使用它...或者只是想要一个稍微好一点的语法(但无法直接使用函数),然后只需创建自己的占位符类型并覆盖它以在运算符 <、> 等上返回 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;
}

它是递归的,并且在某些实现上可能很慢,但有一种方法可以做到这一点。

Make your bool_xxx functions actually functors of a specific kind of type (tag dispatching would be enough). Then override || and && for them such that these operators return a bool_and, or bool_or. Then you can use your bool_ predicates like so:


std::find_if(vect.begin(), vect.end(), bool_x1() || bool_x2() && (bool_x3() || bool_x4() && bool_x5()));

If you're tempted to write a "where" function then you apparently want to do this more than once with a different set of bool_xxx functions. Even if you know that you want a certain type of composition now, you may as well make it as universal as possible. This is how I'd do it.

Edit:

Based on this comment: @Jerry: For example I need to know: id=where(v < 10.0 && v>1.0); and somewhere later I would like to know: id=where(v < fun(v)); you may be better off with 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));

Or, if you hate lambda or aren't allowed to use it...or just want a very slightly nicer syntax (but inability to use functions directly) then just make your own placeholder type and override it to return bool_xxx functors on operators <, >, etc...

Edit2: Here's an untested where that returns a vector of iterators to all objects matching:


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

It's recursive and could be slow on some implementations but there's one way to do it.

街道布景 2024-09-12 04:30:53

您可以使用 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_letterfind_value,我们想要在主向量中搜索匹配项。 transform_iftransform 的谓词版本,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;
}

You could use a predicated version of transform, if there were one. There's not one, but it is very easy to write:

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

Then you would need a way to make a composite of multiple predicates, so that you could express something like find_if( begin, end, condition1 && condition2 ). This, again, is easy to write:

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

Finally you need a gizmo that transform_if uses to convert an object reference to an object pointer. Surprise, surprise, easy to write...

template<typename Obj>  struct get_ptr : public std::unary_function<Obj, Obj*>
{
    Obj* operator()(Obj& rhs) const { return &rhs; }
};

Let's put this all together with a concrete example. Gizmo below is the object that you have a collection of. We have 2 predicates find_letter and find_value that we want to search for matches to in our main vector. transform_if is the predicated version of transform, get_ptr converts an object reference to a pointer, and binary_composite strings together the two composites.

#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:

Based on sbi's iterative approach, here's a predicated version of copy, which is more in line with the general STL paradigm, and can be used with back_insert_iterator to accomplish what's wanted in this case. It will give you a vector of object, not iterators or indexes, so the transform_if I posted above is still better for this use than copy_if. But here it is...

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;
}
海未深 2024-09-12 04:30:53

这似乎是一个可以用 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;
}

This seems like a problem that could much easier be solved in an declarative language like Prolog. I gave it a try in C++ anyway:

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;
}
み青杉依旧 2024-09-12 04:30:53

我不确定你想要哪个索引。这是您想要实现的目标吗:

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

I am not sure which indexes you want. Is this what you are trying to acheive:

//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;
}
青芜 2024-09-12 04:30:53
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>());

诺亚·罗伯特的解决方案很好,但我不完全确定如何才能实现这一点。

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

Overload for additional function object arguments as you wish. Use:

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

Noah Robert's solution is nice, but I'm not wholly sure how I could make that work.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文