Tôi không biết về bất kỳ thực hiện trong một thư viện lớn, nhưng nó trông giống như một vấn đề thú vị vì vậy tôi đã viết một thực hiện cơ bản. Tôi đã chỉ thử nghiệm nó với các trường hợp thử nghiệm tôi trình bày ở đây, vì vậy tôi không khuyên bạn nên sử dụng nó mà không cần thử nghiệm thêm.
Vấn đề phức tạp hơn một chút vì có vẻ như một số vùng chứa "bên trong" có thể trống và bạn phải bỏ qua chúng. Điều này có nghĩa rằng việc nâng cấp flattening_iterator
bởi một vị trí có thể thực sự đẩy trình vòng lặp vào vùng chứa "bên ngoài" bởi nhiều vị trí. Bởi vì điều này, các flattening_iterator
cần phải biết nơi kết thúc của phạm vi bên ngoài là để nó biết khi nó cần phải dừng lại.
Triển khai này là trình lặp chuyển tiếp. Một iterator hai chiều cũng sẽ cần phải theo dõi sự khởi đầu của phạm vi bên ngoài. Các mẫu chức năng flatten
được sử dụng để làm cho việc xây dựng flattening_iterator
s dễ dàng hơn một chút.
#include <iterator>
// A forward iterator that "flattens" a container of containers. For example,
// a vector<vector<int>> containing { { 1, 2, 3 }, { 4, 5, 6 } } is iterated as
// a single range, { 1, 2, 3, 4, 5, 6 }.
template <typename OuterIterator>
class flattening_iterator
{
public:
typedef OuterIterator outer_iterator;
typedef typename OuterIterator::value_type::iterator inner_iterator;
typedef std::forward_iterator_tag iterator_category;
typedef typename inner_iterator::value_type value_type;
typedef typename inner_iterator::difference_type difference_type;
typedef typename inner_iterator::pointer pointer;
typedef typename inner_iterator::reference reference;
flattening_iterator() { }
flattening_iterator(outer_iterator it) : outer_it_(it), outer_end_(it) { }
flattening_iterator(outer_iterator it, outer_iterator end)
: outer_it_(it),
outer_end_(end)
{
if (outer_it_ == outer_end_) { return; }
inner_it_ = outer_it_->begin();
advance_past_empty_inner_containers();
}
reference operator*() const { return *inner_it_; }
pointer operator->() const { return &*inner_it_; }
flattening_iterator& operator++()
{
++inner_it_;
if (inner_it_ == outer_it_->end())
advance_past_empty_inner_containers();
return *this;
}
flattening_iterator operator++(int)
{
flattening_iterator it(*this);
++*this;
return it;
}
friend bool operator==(const flattening_iterator& a,
const flattening_iterator& b)
{
if (a.outer_it_ != b.outer_it_)
return false;
if (a.outer_it_ != a.outer_end_ &&
b.outer_it_ != b.outer_end_ &&
a.inner_it_ != b.inner_it_)
return false;
return true;
}
friend bool operator!=(const flattening_iterator& a,
const flattening_iterator& b)
{
return !(a == b);
}
private:
void advance_past_empty_inner_containers()
{
while (outer_it_ != outer_end_ && inner_it_ == outer_it_->end())
{
++outer_it_;
if (outer_it_ != outer_end_)
inner_it_ = outer_it_->begin();
}
}
outer_iterator outer_it_;
outer_iterator outer_end_;
inner_iterator inner_it_;
};
template <typename Iterator>
flattening_iterator<Iterator> flatten(Iterator it)
{
return flattening_iterator<Iterator>(it, it);
}
template <typename Iterator>
flattening_iterator<Iterator> flatten(Iterator first, Iterator last)
{
return flattening_iterator<Iterator>(first, last);
}
Sau đây là một thử nghiệm sơ khai tối thiểu:
#include <algorithm>
#include <iostream>
#include <set>
#include <vector>
int main()
{
// Generate some test data: it looks like this:
// { { 0, 1, 2, 3 }, { 4, 5, 6, 7 }, { 8, 9, 10, 11 } }
std::vector<std::vector<int>> v(3);
int i(0);
for (auto it(v.begin()); it != v.end(); ++it)
{
it->push_back(i++); it->push_back(i++);
it->push_back(i++); it->push_back(i++);
}
// Flatten the data and print all the elements:
for (auto it(flatten(v.begin(), v.end())); it != v.end(); ++it)
{
std::cout << *it << ", ";
}
std::cout << "\n";
// Or, since the standard library algorithms are awesome:
std::copy(flatten(v.begin(), v.end()), flatten(v.end()),
std::ostream_iterator<int>(std::cout, ", "));
}
Như tôi đã nói ngay từ đầu, tôi đã không kiểm tra này triệt để. Hãy cho tôi biết nếu bạn tìm thấy bất kỳ lỗi nào và tôi sẽ vui lòng sửa chúng.
Nó sẽ in các số từ 1 đến 12, nhưng không nhất thiết phải theo thứ tự vì bạn đang sử dụng tập _unordered_ trong ví dụ, phải không? –
@ James: Vâng, trong ví dụ này tôi không quan tâm thứ tự chúng được in. – George