繁体   English   中英

C ++ STL范围容器

[英]C++ STL Range Container

我正在寻找一个从双指针映射到对象指针的容器。 但是,每个键只是一个与该对象相对应的双精度范围。

例如,可能有一个键值/值对<(0.0 3.0),ptr>或<(3.5 10.0),ptr2>

container [1.0]应该返回ptr,container [3.0]也应该返回ptr,而容器[-1.0]应该是未定义的。

默认情况下是否有任何具有类似行为的对象或我是否必须自己实现?

编辑

这是我编写的实际代码,可能更容易调试/提供建议。

// Behavior: A range is defined mathematically as (min, max]

class dblRange
{
public:
    double min;
    double max;

    dblRange(double min, double max)
    {
        this->min = min;
        this->max = max;
    };

    dblRange(double val)
    {
        this->min = val;
        this->max = val;
    };

    int compare(const dblRange rhs)
    {
        // 1 if this > rhs
        // 0 if this == rhs
        //-1 if this < rhs
        if (rhs.min == rhs.max && min == max)
        {
            /*if (min > rhs.min)
                return 1;
            else if (min == rhs.min)
                return 0;
            else
                return -1;*/
            throw "You should not be comparing values like this. :(\n";
        }
        else if (rhs.max == rhs.min)
        {
            if (min > rhs.min) 
                return 1;
            else if (min <= rhs.min && max > rhs.min)
                return 0;
            else // (max <= rhs.min)
                return -1;
        }
        else if (min == max)
        {
            if (min >= rhs.max)
                return 1;
            else if (min < rhs.max && min >= rhs.min)
                return 0;
            else // if (min < rhs.min
                return -1;
        }

        // Check if the two ranges are equal:
        if (rhs.min == min && rhs.max == max)
        {
            return 0;
        }
        else if (rhs.min < min && rhs.max <= min)
        {
            // This is what happens if rhs is fully lower than this one.
            return 1;
        }
        else if (rhs.min > min && rhs.min >= max)
        {
            return -1;
        }
        else
        {
            // This means there's an undefined case. Ranges are overlapping, 
            // so comparisons don't work quite nicely.

            throw "Ranges are overlapping weirdly. :(\n";
        }
    };

    int compare(const dblRange rhs) const
    {
        // 1 if this > rhs
        // 0 if this == rhs
        //-1 if this < rhs
        if (rhs.min == rhs.max && min == max)
        {
            /*if (min > rhs.min)
                return 1;
            else if (min == rhs.min)
                return 0;
            else
                return -1;*/
            throw "You should not be comparing values like this. :(\n";
        }
        else if (rhs.max == rhs.min)
        {
            if (min > rhs.min) 
                return 1;
            else if (min <= rhs.min && max > rhs.min)
                return 0;
            else // (max <= rhs.min)
                return -1;
        }
        else if (min == max)
        {
            if (min >= rhs.max)
                return 1;
            else if (min < rhs.max && min >= rhs.min)
                return 0;
            else // if (min < rhs.min
                return -1;
        }

        // Check if the two ranges are equal:
        if (rhs.min == min && rhs.max == max)
        {
            return 0;
        }
        else if (rhs.min < min && rhs.max <= min)
        {
            // This is what happens if rhs is fully lower than this one.
            return 1;
        }
        else if (rhs.min > min && rhs.min >= max)
        {
            return -1;
        }
        else
        {
            // This means there's an undefined case. Ranges are overlapping, 
            // so comparisons don't work quite nicely.

            throw "Ranges are overlapping weirdly. :(\n";
        }
    };

    bool operator== (const dblRange rhs ) {return (*this).compare(rhs)==0;};
    bool operator== (const dblRange rhs ) const {return (*this).compare(rhs)==0;};
    bool operator!= (const dblRange rhs ) {return (*this).compare(rhs)!=0;};
    bool operator!= (const dblRange rhs ) const {return (*this).compare(rhs)!=0;};
    bool operator< (const dblRange rhs ) {return (*this).compare(rhs)<0;};
    bool operator< (const dblRange rhs ) const {return (*this).compare(rhs)<0;};
    bool operator> (const dblRange rhs ) {return (*this).compare(rhs)>0;};
    bool operator> (const dblRange rhs ) const {return (*this).compare(rhs)>0;};
    bool operator<= (const dblRange rhs ) {return (*this).compare(rhs)<=0;};
    bool operator<= (const dblRange rhs ) const {return (*this).compare(rhs)<=0;};
    bool operator>= (const dblRange rhs ) {return (*this).compare(rhs)>=0;};
    bool operator>= (const dblRange rhs ) const {return (*this).compare(rhs)>=0;};

};

现在我很难让地图接受一个双键作为键,即使定义了比较运算符。

这里有一些驱动代码,我用它来测试它是否可行:

std::map<dblRange, int> map;
map[dblRange(0,1)] = 1;
map[dblRange(1,4)] = 2;
map[dblRange(4,5)] = 3;

map[3.0] = 4;

我主要同意Earwicker,因为你可以定义一个范围。 现在,我赞成实现具有真正含义的运算符(执行基本类型的操作:如果两个范围相等,则两个范围比较相等)。 然后,您可以使用第三个map参数将比较函数(或函数)传递给它,以解决此映射的特定问题。

// Generic range, can be parametrized for any type (double, float, int...)
template< typename T >
class range
{
public:
    typedef T value_type;

    range( T const & center ) : min_( center ), max_( center ) {}
    range( T const & min, T const & max )
        : min_( min ), max_( max ) {}
    T min() const { return min_; }
    T max() const { return max_; }
private:
    T min_;
    T max_;
};

// Detection of outside of range to the left (smaller values):
//
// a range lhs is left (smaller) of another range if both lhs.min() and lhs.max() 
// are smaller than rhs.min().
template <typename T>
struct left_of_range : public std::binary_function< range<T>, range<T>, bool >
{
    bool operator()( range<T> const & lhs, range<T> const & rhs ) const
    {
        return lhs.min() < rhs.min()
            && lhs.max() <= rhs.min();
    }
};
int main()
{
    typedef std::map< range<double>, std::string, left_of_range<double> > map_type;

    map_type integer; // integer part of a decimal number:

    integer[ range<double>( 0.0, 1.0 ) ] = "zero";
    integer[ range<double>( 1.0, 2.0 ) ] = "one";
    integer[ range<double>( 2.0, 3.0 ) ] = "two";
    // ...

    std::cout << integer[ range<double>( 0.5 ) ] << std::endl; // zero
    std::cout << integer[ range<double>( 1.0 ) ] << std::endl; // one
    std::cout << integer[ 1.5 ] << std::endl; // one, again, implicit conversion kicks in
}

您必须小心双重值之间的相等和比较。 获得相同值(在现实世界中)的不同方式可以产生略微不同的浮点结果。

创建一个DoubleRange类来存储双范围,并在其上实现比较运算符。 这样, std::map将为您完成剩下的工作, DoubleRange类作为键。

最好使用Interval树数据结构。 Boost在Interval容器库中有一个实现

一种方法是先计算“断点”:

typedef vector< tuple<double, double, foo*> > collisionlist_t;
const collisionlist_t vec;
vec.push_back(make_tuple(0.0, 3.0, ptr));
vec.push_back(make_tuple(3.5, 10.0, ptr2));
// sort 
std::map<double, foo*> range_lower_bounds;
for(collisionlist_t::const_iterator curr(vec.begin()), end(vec.end()); curr!=end; ++curr)
{
    /* if ranges are potentially overlapping, put some code here to handle it */
    range_lower_bounds[curr->get<0>()] = curr->get<2>();
    range_lower_bounds[curr->get<1>()] = NULL;
}

double x = // ...
std::map<double, foo*>::const_iterator citer = range_lower_bounds.lower_bound(x);

另一个建议:使用数学变换将索引从REAL映射到INT,可以直接比较。

如果这些范围是多重且密集的,那么也有一种称为“间隔树”的结构可能有所帮助。

如果您的间隔必须不重叠,则必须添加一些额外的代码以在插入时验证此属性。 具体来说,您要声明的属性是您的新间隔完全在以前为空的范围内。 一种简单的方法是允许两种类型的范围:“占用”和“空”。 您应该首先创建一个覆盖整个可用范围的“空”条目。 插入新的“占用”范围需要:

(1)在新范围内查找一些值。
(2)确保返回的范围为空,并完全包含您的新范围。 (这是上面要求的断言)
(3)修改返回的空范围,使其结束位于新范围的开头。 (4)插入一个新的空范围,该空范围从新范围的末尾开始,并在返回范围的旧端结束。
(5)插入新范围,确信它被空范围包围。
(6)插入新的占用范围时可能会有额外的角落情况,该范围没有空的空间将其与其他占用范围分开。

间隔是打开还是关闭或半开? 我会假设关闭。 请注意,间隔不能与地图的定义重叠。 当插入重叠间隔时,您还需要拆分规则。 规则需要决定拆分发生的位置,并且必须考虑浮点epsilon。

此实现使用map :: lower_bound,并且不使用类作为地图的域

map :: lower_bound将迭代器返回到映射中第一个元素,其键值等于或大于指定键的键值。 (即,最小键大于或等于K.不幸的选择STL方法名称,因为它是K的最小上限。)

template <class codomain>
class RangeMap : private std::map<double,std::pair<double,codomain>{
public:
    typedef double domain;
    typedef std::map<double,std::pair<double,codomain>:: super;
    typename super::value_type value_type;
protected:
    static domain& lower(const value_type& v){
        return v.first;
    }

    static domain& upper(const value_type& v){
        return v.second.first;
    }

    static codomain& v(const value_type& v){
        return v.second.second;
    }

public:

    static const domain& lower(const value_type& v){
        return v.first;
    }
    static const domain& upper(const value_type& v){
        return v.second.first;
    }
    static const codomain& v(const value_type& v){
        return v.second.second;
    }


    static bool is_point(const value_type& vf) {
        return lower(v) == upper(v);
    }

    static bool is_in(const domain& d,const value_type& vf) {
        return (lower(v) <= d) && (d <= upper(v));
    }


    const_iterator greatest_lower_bound(const domain& d)const {
        const_iterator j = super::lower_bound(d);
        if(j!=end() && j->first==d) return j;//d is the lh side of the closed interval
                                             //remember j->first >= d because it was lower but its the first
        if(j==begin()) return end();//d < all intervals
        --j;                        //back up
        return j;
    }
    const_iterator find(domain& d) {
        const_iterator j = greatest_lower_bound(d);
        if (is_in(j,d)) return j;
        return end();
    }
    iterator greatest_lower_bound(const domain& d) {
        iterator j = super::lower_bound(d);
        if(j!=end() && j->first==d) return j;//d is the lh side of the closed interval
                                             //remember j->first >= d because it was lower but its the first
        if(j==begin()) return end();//d < all intervals
        --j;                        //back up
        return j;
    }
    const_iterator find(domain& d) const{
        iterator j = greatest_lower_bound(d);
        if (is_in(j,d)) return j;
        return end();
    }  //so much for find(d) 
    iterator find(domain& d){
        iterator j = greatest_lower_bound(d);
        if (is_in(j,d)) return j;
        return end();
    }  //so much for find(d) 

     struct overlap: public std::exception{
     };
     bool erase(const double lhep,const double rhep);
     //you have a lot of work regarding splitting intervals erasing when overlapped
     //but that can all be done with erase, and insert below. 
     //erase may need to split too
     std::pair<iterator,bool>
     split_and_or_erase_intervals(const double lhep,
                                  const double rhep, 
                                  const codomain& cd);
     //the insert method - note the addition of the overwrtite 
     std::pair<iterator,bool>
     insert(const double lhep,const double rhep,const codomain& cd,bool overwrite_ok){
          if( find(lhep)!=end() || find(rhep)!=end() ) {
              if(overwrite_ok){
                 return split_and_or_erase_intervals(const double lhep,
                                                     const double rhep, 
                                                     const codomain& cd);
              }
              throw overlap();
          }
          return insert(value_type(lhep,pair<double,codomain>(rhep,cd)));
     }
 };

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM