版权所有 © 2007-2010 Joachim Faulhaber
版权所有 © 1999-2006 Cortex Software GmbH
根据 Boost 软件许可证,版本 1.0 进行分发。(参见随附文件 LICENSE_1_0.txt 或复制于 https://boost.ac.cn/LICENSE_1_0.txt)
目录
注意 | |
---|---|
**C++03 支持** 在 **Boost.Icl** 1.84 中已弃用,并将在 **Boost.Icl 1.86** 中移除。 |
区间在软件开发中几乎无处不在。然而,它们很容易通过一对数字编码到用户定义的类中,因此它们在大多数情况下只是 *隐式地* 使用。区间的含义很简单。它们表示上下限之间的所有元素,因此是一个集合。但与集合不同的是,区间通常不能添加到单个新区间中。如果您想将区间添加到仍然表示 *集合* 的区间集合中,您就会想到该库提供的 *区间集* 的概念。
**ICL** 的区间容器最初是在 Cortex Software GmbH 开发的,用于解决与医院信息系统中日期和时间区间计算相关的问题。具有关联值(如 *发票金额* 或 *治疗集*)的时间区间必须在统计、计费程序和治疗调度程序中进行操作。因此,**ICL** 应运而生。它从日期和时间问题域中提取有助于解决常见问题的通用代码,并且也可以应用于其他领域。
区间容器最有利的方面之一是它们对集合和映射的非常紧凑的表示。如果在给定的问题域中,元素通常以连续的块出现,则使用 *元素* 的集合和映射可能会非常低效。除了可以大幅降低空间和时间成本的关联容器的紧凑表示之外,ICL 还提供了一种通用的聚合机制,允许在插入时区间重叠时以有意义的方式组合关联值。
有关简要介绍和概述,您可以查看 **BoostCon2009** 上关于 **ICL** 的演示文稿资料 *。
**区间容器库 (ICL)** 提供 区间
和两种区间容器:区间集
和 区间映射
。
区间集
和 区间映射
在其接口中暴露出两个不同的方面:(1) 集合或映射的功能,这是更 ***抽象的方面***。以及 (2) 区间容器的功能,这是更具体的和 ***与实现相关的方面***。在实践中,这两个方面都是有用的,因此都得到支持。
第一个方面,称为 ***基本*** ***方面***,是更重要的一个。这意味着我们可以像使用 ***元素*** 的集合或映射一样使用 区间集* 或
区间映射
。它公开了相同的功能。
interval_set<int> mySet; mySet.insert(42); bool has_answer = contains(mySet, 42);
第二个方面,称为 ***分段*** ***方面***,允许利用 区间集
和 区间映射
的元素聚集在 ***区间*** 或 ***段*** 中的事实,我们可以对其进行迭代。
// Switch on my favorite telecasts using an interval_set interval<seconds>::type news(make_seconds("20:00:00"), make_seconds("20:15:00")); interval<seconds>::type talk_show(make_seconds("22:45:30"), make_seconds("23:30:50")); interval_set<seconds> myTvProgram; myTvProgram.add(news).add(talk_show); // Iterating over elements (seconds) would be silly ... for(interval_set<seconds>::iterator telecast = myTvProgram.begin(); telecast != myTvProgram.end(); ++telecast) //...so this iterates over intervals TV.switch_on(*telecast);
当集合的元素以连续块的形式出现时,使用 区间集
和 区间映射
可能是有益的:区间
。这在许多问题域中显然是这种情况,尤其是在处理与日期和时间相关的计算的领域中。
与 std::sets
和 maps
不同,区间集 (interval_sets)
和 区间映射 (interval_maps)
实现了 Addable
(可加性)和 Subtractable
(可减性)概念。因此,区间集
定义了一个 operator +=
运算符,它自然地实现为**_集合并集_**,以及一个 operator -=
运算符,它相应地实现为**_集合差集_**。在 **Icl** 中,区间映射
也是可加和可减的。当向 区间映射
中插入一个区间值对导致该区间值对与 区间映射
中已有的区间值对发生冲突时,将加法或减法传播到 区间映射
的关联值的概念被证明是非常有效的。这种运算传播称为**_重叠聚合_**。
这是一个关于小型聚会的第一个示例,它演示了 区间映射
上的**_重叠聚合_**原则。
在示例中,Mary 首先进入派对。她在时间间隔 [20:00,22:00)
内参加。Harry 稍后进入。他在 [21:00,23:00)
内停留。
typedef std::set<string> guests; interval_map<time, guests> party; party += make_pair(interval<time>::right_open(time("20:00"), time("22:00")), guests("Mary")); party += make_pair(interval<time>::right_open(time("21:00"), time("23:00")), guests("Harry")); // party now contains [20:00, 21:00)->{"Mary"} [21:00, 22:00)->{"Harry","Mary"} //guest sets aggregated on overlap [22:00, 23:00)->{"Harry"}
**_在区间重叠时_**,相应的名称集合会被**_累加_**。在**_重叠点_**处,区间会被**_分割_**。区间重叠时的内容累加是通过 operator +=
运算符完成的,该运算符必须为 区间映射
的关联值实现。在本例中,关联值是 guest sets
(客人集合)。因此,guest set
必须将 operator +=
实现为集合并集。
从示例中可以看出,区间映射
既具有**_分解行为_**(在时间维度上),也具有**_累加行为_**(在关联值上)。
可加性和重叠聚合是 区间映射
上的有用特性,它们通过函数 add
和 operator +=
实现。但您也可以将它们与_传统的_ 插入语义一起使用,其行为类似于针对区间插入进行泛化的 std::map::insert
。
除了区间容器之外,我们还可以使用与区间容器**_行为等效_**的元素容器:在基本方面,它们具有完全相同的功能。STL 的 std::set
就是这样一个等效的集合实现。由于 icl 的区间映射的聚合功能,std::map
从根本上来说与 区间映射
并不完全等效。因此,icl 中有一个额外的 icl::map
类模板用于元素映射。
下表概述了 **icl** 提供的主要类模板。
静态边界区间始终具有相同类型的区间边界,例如,右开区间
的右开边界 [a..b)
。动态边界区间可以具有不同的边界。有关详细信息,请参阅关于 **_区间_** 的章节。
Std::set
放在括号中,因为它不是 **ICL** 的类模板。它可以用作元素容器,在其基本方面与 ICL 的区间集行为等效。“样式”列指的是区间容器组合已添加区间的方式。下一节将通过示例说明这些**_组合样式_**。
当我们将区间或区间值对添加到区间容器时,可以以不同的方式添加区间:可以连接、分割或保持区间分离。下表通过示例展示了不同的区间组合样式。
表 1.4. 区间组合样式示例
连接 |
分离 |
分割 |
|
---|---|---|---|
集合 |
|
|
|
{[1 3) } + [2 4) + [4 5) = {[1 5)}
|
{[1 3)} } + [2 4) + [4 5) = {[1 4)[4 5)}
|
{[1 3) } + [2 4) + [4 5) = {[1 2)[2 3)[3 4)[4 5)}
|
|
映射 |
|
|
|
{[1 3)->1 } + [2 4)->1 + [4 5)->1 = {[1 2)[2 3)[3 5) } | ->1 ->2 ->1 |
|
{[1 3)->1 } + [2 4)->1 + [4 5)->1 = {[1 2)[2 3)[3 4)[4 5) } | ->1 ->2 ->1 ->1 |
|
请注意,`interval_sets` *A*、*B* 和 *C* 表示相同的元素集 `{1,2,3,4}`,`interval_maps` *D* 和 *E* 表示相同的元素映射 `{1->1, 2->2, 3->1, 4->1}`。有关其他演示,请参见示例程序 区间容器。
区间集
和 区间映射
始终处于**_最小表示_**状态。这在许多情况下非常有用,在这些情况下,区间的插入点或交点无关紧要。因此,在大多数情况下,区间集
和 区间映射
将是区间容器的首选。
Split_interval_set
和 split_interval_map
正好相反,它们具有插入记忆。它们会累积来自添加和交集的区间边界。如果我们想要使用特定的时间网格(例如,月份或日历周,或两者)来丰富区间容器,这将特别有用。参见示例 月和周的时间网格。
Separate_interval_set
实现了分离样式。这种样式保留了永远不会被重叠区间穿过的边界。因此,如果插入到 separate_interval_set
中的所有区间都是从某个永远不会穿过月份边界的网格生成的,那么这些边界将在 separate_interval_set
中保留。
14:46 15.10.2010