为什么 boost::icl::interval_map 不加起来呢?

Why wouldn't boost::icl::interval_map add up the value?

考虑以下程序:

#include <iostream>
#include <boost/icl/interval_map.hpp>

struct Value
{
    explicit Value(int v) : v(v), is_empty(false) {}
    Value() : v(0), is_empty(true) {}

    Value& operator+=(const Value& other)
    {
        v += other.v;
        return *this;
    }

    bool operator==(const Value& other) const { return is_empty == other.is_empty; }
    bool operator!=(const Value& other) const { return is_empty != other.is_empty; }

    int v;
    bool is_empty;
};

int main()
{
    boost::icl::interval_map<int, Value> m;
    m.add(std::make_pair(boost::icl::interval<int>::right_open(10, 20), Value(2)));
    m.add(std::make_pair(boost::icl::interval<int>::right_open(15, 30), Value(3)));
    std::cout << m.iterative_size() << '\n';
    std::cout << m.begin()->first.lower() << '\n';
    std::cout << m.begin()->first.upper() << '\n';
    std::cout << m.begin()->second.v << '\n';
}

输出为

1
10
30
2

问题:


我想要以下结果:

{([10,20)->(2))+
      ([15,30)->(3))+ 
                       ([40,50)->(11))}
=
{([10,30)->(5))([40,50)->(11))}

也就是说,当区间相加时,它们被合并,合并后的值被存储为整个合并的区间。

这个操作可能没有数学意义,但这是我程序中需要的。 (此外,我不会减去间隔)。

这是我自己对最后一个问题的务实回答的尝试:

How to achieve the behavior that intervals are added and the value from += is preserved?

我找到了另一个 implementation,它更简单,但可以很容易地根据我想要的行为进行定制。它采用区间类型作为模板参数,并使用方法 join 来连接区间,所以我用我自己的区间类型专门化它,它添加值并在连接上求和值:

#include <iostream>
#include "interval_tree.hpp"

using iv_base = lib_interval_tree::interval<int, lib_interval_tree::right_open>;
struct iv_t : iv_base
{
    int value;

    iv_t(const iv_base& base, int v)
        : iv_base(base)
        , value(v)
    {
    }

    iv_t(value_type low, value_type high, int v)
        : iv_base(low, high)
        , value(v)
    {
    }

    iv_t join(iv_t const& other) const
    {
        return iv_t(iv_base::join(other), value + other.value);
    }
};

using it_t = lib_interval_tree::interval_tree<iv_t>;

int main()
{
    it_t it;

    it.insert_overlap(iv_t( 10, 20, 2 ));
    it.insert_overlap(iv_t{ 15, 30, 3 });
    it.insert_overlap(iv_t{ 40, 50, 11 });

    for (decltype(auto) v : it)
    {
        std::cout << v.low() << ".." << v.high() << ":" << v.value << "\n";
    }
}

输出为:

10..30:5
40..50:11

完全可能 Boost.Icl 可以针对相同的行为进行自定义,但要了解是否可能以及如何进行更复杂。

问题在于

bool operator==(const Value& other) const { return is_empty == other.is_empty; }
bool operator!=(const Value& other) const { return is_empty != other.is_empty; }

使任何值“相同”。这使得行为未指定,并且可能最终合并任何接触间隔并保持先前​​的值(因为根据您自己的operator==它是“相等的”)。

让我们用 C++ 生成更正确的比较:

struct Value {
    explicit Value(int v) : value(v) {}
    Value() : is_empty(true) {}

    Value& operator+=(const Value& other) { value += other.value; return *this; }

    auto operator<=>(Value const&) const = default;

    bool is_empty = false;
    int  value    = 0;
};

现在你可以Live On Compiler Explorer

#include <iostream>
#include <boost/icl/interval_map.hpp>
int main()
{
    namespace icl = boost::icl;
    using I = icl::interval<int>;
    using Value = int;

    auto const a = std::make_pair(I::right_open(10, 20), Value(2));
    auto const b = std::make_pair(I::right_open(15, 30), Value(3));
    auto const c = std::make_pair(I::right_open(40, 50), Value(11));

    icl::interval_map<int, Value> m;
    m.add(a);
    m.add(b);
    m.add(c);

    std::cout << m << "\n";

    m.subtract(a);

    std::cout << m << "\n";
}

打印

{([10,15)->(2))([15,20)->(5))([20,30)->(3))([40,50)->(11))}

但是,我很难理解 Valueoptional<int> 上添加了什么,实际上已经是 interval_map 的行为了:

int main()
{
    namespace icl = boost::icl;
    using I = icl::interval<int>;

    auto const a = std::make_pair(I::right_open(10, 20), 2);
    auto const b = std::make_pair(I::right_open(15, 30), 3);
    auto const c = std::make_pair(I::right_open(40, 50), 11);

    icl::interval_map<int, int> m;
    m.add(a);
    m.add(b);
    m.add(c);

    std::cout << m << "\n";

    m.subtract(a);

    std::cout << m << "\n";
}

打印:

{([10,15)->2)([15,20)->5)([20,30)->3)([40,50)->11)}
{([15,30)->3)([40,50)->11)}

事实上,在某些方面,您的习惯 Value 对我来说似乎是“错误的”,evem 与固定比较:

Live On Compiler Explorer

#include <iostream>
#include <boost/icl/interval_map.hpp>

struct Value {
    explicit Value(int v) : value(v) {}
    Value() : is_empty(true) {}

    Value& operator+=(const Value& other) { value += other.value; return *this; }
    Value& operator-=(const Value& other) { value -= other.value; return *this; }

    auto operator<=>(Value const&) const = default;

    bool is_empty = false;
    int  value    = 0;

    friend std::ostream& operator<<(std::ostream& os, Value const& v) {
        return v.is_empty ? os << "#EMPTY" : os << v.value;
    }
};

int main()
{
    namespace icl = boost::icl;
    using I = icl::interval<int>;

    auto const a = std::make_pair(I::right_open(10, 20), Value(2));
    auto const b = std::make_pair(I::right_open(15, 30), Value(3));
    auto const c = std::make_pair(I::right_open(40, 50), Value(11));

    icl::interval_map<int, Value> m;
    m.add(a);
    m.add(b);
    m.add(c);

    std::cout << m << "\n";

    m.subtract(a);

    std::cout << m << "\n";
}

打印

{([10,15)->2)([15,20)->5)([20,30)->3)([40,50)->11)}
{([10,15)->0)([15,30)->3)([40,50)->11)}

[10,15)->0 是真的想要的吗?