Interval range library

"C++11/14 Advanced Programming: Exploring the Boost Library" Notes

The range library abstracts the concept of "range" from iterators and containers. It is based on iterators and containers, but its requirements are much lower than containers. It does not need to accommodate elements and only contains the two first and end point positions of the range. Vector is an interval, string is an interval, but stack is not an interval, a pair is also an interval, and an array is also an interval. You can use the meta-function has_range_iterator to determine whether a type is an interval.
The intervals are closed on the left and open on the right. You can use the member function begin()/end() or the free function begin()/end() to obtain its two endpoints.

Characteristic functions of the range library

  • range_iterator<R>: the iterator type that returns the range
  • range_value<R>: Returns the value type of the interval
  • range_reference<R>: Returns the reference type of the range
  • range_pointer<R>: Returns the pointer type of the range
  • range_category<R>: Returns the iterator category of the range
  • range_size<R>: Returns the length type of the interval (unsigned integer)
  • range_difference<R>: Returns the distance type of the interval (signed integer)
  • range_reverse_iterator<R>: Returns the reverse iterator of the interval (only for bidirectional intervals)

Operation functions of range library

  • begin(): Returns the starting point of the interval
  • end(): Returns the end point of the interval
  • rbegin(): Returns the starting point of the reverse interval (bidirectional interval)
  • rend(): Returns the end point of the reverse interval (bidirectional interval)
  • empty(): Determine whether the interval is empty
  • distance(): Returns the distance between the two ends of the interval
  • size(): Returns the size of the interval

begin() and end() provide convenient operations for classes that conform to the interval concept without begin() and end() member functions, such as arrays. These two functions have been included in the C++11 standard.


standard algorithm

The range library provides range versions of all standard algorithms in the namespace boost, divided into three header files:

  • <boost/range/algorithm.hpp>: All standard algorithms in C++
  • <boost/range/algorithm_ext.hpp>: Some extended algorithms, such as erase, itoa
  • <boost/range/numeric.hpp>: Numerical standard algorithm

The parameters of these interval algorithms are basically the same as the standard algorithm except that the two iterators are changed to one interval. It only reduces the code to write the positions of the two iterators, making it simpler:

std::vector<int> v{
   
   8,16,1,3,7,3,42}
assert(boost::count(v,3) == 2);     //统计元素数量
//实际调用标准算法std::count(boost::begin(v),boost::end(v),3)
assert(boost::find(v,7) != v.end());    //查找元素
boost::sort(v);

Basically, these interval algorithms are divided into two categories according to the return type. The first category of algorithms returns the original interval. They are generally variable algorithms, process the entire interval, and then return to the original interval. The return value can continue to be directly used by other algorithms.

std::vector<int> v(10);
boost::rand48 rnd;
boost::sort(                       //3.排序
    boost::random_shuffle(         //2.随机打乱
        boost::generate(v,rnd)));  //1.用随机数填充区间

(Variability algorithms include the following: fill/fill_n, generate/generate_n, inplace_merge, random_shuffle, replace/replace_if, reverse/rotate, sort/stable_sort, partial_sort/nth_element, make_heap/sort_heap/push_heap/pop_heap)

The second type of algorithm uses a template parameter to customize the returned interval, and is generally related to some form of search/split operation. The ordinary versions of these algorithms return a processed iterator position (such as the find algorithm). We can call this iterator position found. Based on the found position, we can get two commonly used subintervals: [begin(rng), found] and [found,end(rng)]. In addition to the found position, the range library also defines the next (found) and prior (found) positions, which are divided into four parts together with the start and end points.
The range library also declares an enumeration type range_return_value in the boost namespace as a template parameter for the interval algorithm to customize the returned interval type for meta-computation:

enum range_return_value
{
    return_found,
    return_next,
    return_prior,
    return_begin_found,
    return_begin_next,
    return_begin_prior,
    return_found_end,
    return_next_end,
    return_prior_end,
    return_begin_end
};

Iterator range class

The concept of interval is essentially a pair of iterators, which can be represented by std::pair<I,I>, but std::pair<I,I> is too simple and cannot clearly express the meaning of the interval, so the range library Provide a specialized range class: iterator_range, which encapsulates two iterators, has a more standardized interface and is lightweight.

std::vector<int> v(10);
typedef iterator_range<vector<int>::iterator> vec_range; //区间类型定义

vec_range r1(v);    //从容器构造一个区间
assert(!r1.empty());
assert(r1.size() == 10);

int a[10];
typedef iterator_range<int*> int_range;
int_range r2(a,a+5);  //从两个迭代器构造区间
assert(r2.size() == 5);

The range library provides the factory function make_iterator_range(), which can be used with the keyword auto to save the trouble of manually specifying the iterator type when creating iterator_range:

std::vector<int> v(10);
auto r1 = make_iterator_range(v);   //从区间构造
assert(has_range_iterator<decltype(r1)>::value);

int a[10];
auto r2 = make_iterator_range(a,a+5);   //从迭代器构造

auto r3 = make_iterator_range(a,1,-1);  //从指定迭代器的位置
assert(r3.size() == 8)

There is also a template function copy_range in the range library, which can copy the elements in the range to a new container:

char a[] = "iterator range";
auto r = boost::find_first(a," ");  //使用find_first字符串算法
assert(r.front() == ' ');           //返回查找到的区间
auto r2 = make_iterator_range(a,r.begin()); //取字符串的前半部分
assert(copy_range<string>(r2) == "iterator");

range library auxiliary tools

Many auxiliary tools (functions) are introduced, but the essence of their uses cannot be obtained, and most of them are similar to iterator tools.

  • sub_range
    is a subclass of iterator_range. Its purpose is to represent a subrange, which is different from iterator_range. The template parameter of sub_range is a forward interval type, and iterator_range is an iterator type. sub_range has all the functions of iterator_range
  • counting_range
    is similar to counting_iterator, returning a counting iterator range
  • istream_range
    encapsulates the input stream iterator std::istream_iterator, which converts the input process of the input stream into a range
  • irange
    is similar to counting_range, with the added function of specifying step sizes.
  • combined_range
    uses zip_iterator to "package" multiple intervals into one interval, which is equivalent to the interval-enhanced version of zip_iterator.
  • any_range
    uses type erasure technology and can be applied to "any" containers that meet the requirements. This is a bit interesting, let me give you an example.
    A summary of its classes is as follows:
template<
    class Vaule,                     //值类型
    class Traversal,                 //迭代器便利类型
    class Reference = Value&,        //引用类型
    class Difference = std::ptrdiff_t,
    class Buffer = use_default
>
class any_range: public iterator_range<...>
{...};

The first two template parameters determine the value type and traversal type of the interval respectively. You do not need to care about the specific type of the container under the interval. As long as the container meets the requirements of Value and Traversal, for example, a single-pass integer any_range interval can be used. Handle containers such as lists, vectors, and even sets at will.

typedef any_range<int,          //区间的值类型是int
    boost::single_pass_traversal_tag> range_type;  //区间要求可单向遍历

list<int> l = {
   
   1,3,5,7,9};
range_type r(l);
for(const auto& x:r)
{ cout << x << ","; }

vector<int> v = {
   
   2,4,6,8,0};
r = v;
for(const auto& x:r)
{ cout << x << ","; }

The range library also provides a factory metafunction, note that it is a metafunction, which can generate a suitable any_range from an interval or container type:

template<
    class WrappedRange,             //区间要包装的类型
    class Value = use_default,      //值类型
    class Traversal = use_default,  //迭代器遍历类型
    class Reference = use_default,  //引用类型
    class Difference = use_default,
    class Buffer = use_default
>
struct any_range_type_generator
{
    typedef any_range<...> type;   //元计算返回any_range
};

any_range_type_generator only needs to provide the first template parameter interval or container type, and the remaining values ​​will be automatically deduced. Finally, use ::type to get the any_range type, such as: However, any_range_type_generator needs to provide the
typedef any_range_type_generator<decltype(l)>::type range_type
interval or container type, which reduces the adaptability of any_range, resulting in It can only be used on containers with specific Traversal attributes. For example, any_range generated using the list type above cannot be used on vector.


interval adapter

Adapt one interval to another interval.
Similar to iterators, the range adapter uses boost::copy to drive the flow of data through ranges and iterators. Due to the self-expression characteristics of ranges, it also overloads operator| () to support multiple connections in a form similar to the UNIX pipe operator. intervals, each interval performs a certain amount of work.

The range adapter of the range library is located in the namespace boost::adaptors and requires the header file <boost/range/adaptos.hpp>. The
range adapters provided by the range library include the following:

  • adjacent_filtered(P): Use predicate P to filter two adjacent elements
  • copied(n,m): takes the subinterval [n,m), can only be used for random access intervals
  • filtered(P): Filter elements using predicate P
  • indexed(i): Add an index number starting from i to the iterator
  • map_keys: Take out the keys in the map container
  • map_values: Take out the value in the map container
  • replaced(x,y): Replace x in the interval with y
  • replaced_if(P,v): Replace the elements in the interval that match the predicate P with v
  • reversed: reverse order interval
  • sliced(n,m):同copied
  • strided(n): Jump forward in the interval with a step size n
  • tokenized(): Use boost::regex for regular processing
  • transformed(F): similar to the std::transform algorithm, using F to process each element
  • uniqued: Filter out adjacent duplicate elements

When using it, imagine the original interval as a data source. After passing through the adapter, the processed data is obtained. Finally, the algorithm processes the data, which is somewhat similar to stream processing:

std::vector<int> v{
   
   7,8,4,6,53,2,6};
boost::copy(                //copy算法
    boost::sort(v) |        //先排序,区间改变
        adaptors::uniqued,  //去重,原区间不变
    ostream_iterator<int>(std::cout,","));  //去重后数据输出到标准流
assert(boost::count(v,6) == 2);     //原区间排序但未去重

auto even = [](int x){ return x%2 == 0; };
assert(boost::distance(      //计算区间长度
        v | adaptors::filtered(even)) == 5);  //仅计算偶数

Interval algorithms and interval adapters can be flexibly combined, but be aware that some combinations are undesirable, such as v | filtered | copied, i filtered returns a bidirectional interval - even if the original interval is a random access interval, both copied and sliced The requirement is random access ranges, and forced matching will cause compilation errors.


connecting interval

There is also a more commonly used function join() in the range library, which can connect two intervals into one interval without requiring the two intervals to be adjacent.

std::vector<int> v;
boost::copy(boost::irange(0,10),std::back_inserter(v));

auto r1 = make_iterator_range(v.begin(),v.begin() + 3); //子区间0~2
auto r2 = make_iterator_range(v.begin() + 5,v.end()); //子区间5~9

auto r3 = boost::join(r1,r2);
//输出0,1,2,5,6,7,8,9
boost::copy(r3,ostream_iterator<int>(cout,","));

おすすめ

転載: blog.csdn.net/zuolj/article/details/78661496