Ranges 实战进阶
Ranges 实战进阶

🚀 Ranges 实战进阶:滑动窗口、zip_view、协程结合与性能分析


🧊 1. 实战:用 ranges 实现滑动窗口最大值(Monotonic Queue)

滑动窗口问题是算法题常客,比如 LeetCode 239:

给定数组 nums 和窗口大小 k,返回每个窗口内的最大值。

我们可以使用 ranges + 自定义 view 实现一个惰性滑动窗口视图。

✨ 目标接口:

1auto r = sliding_window(v, 3);

🧩 自定义 sliding_window view:

 1template <std::ranges::view V>
 2requires std::ranges::forward_range<V>
 3class sliding_window_view : public std::ranges::view_interface<sliding_window_view<V>> {
 4    V base_;
 5    size_t k_;
 6
 7public:
 8    sliding_window_view(V base, size_t k) : base_(std::move(base)), k_(k) {}
 9
10    struct iterator {
11        std::ranges::iterator_t<V> first, last, end;
12        size_t k;
13
14        bool operator!=(const iterator& other) const {
15            return first != other.first;
16        }
17
18        auto operator*() const {
19            return std::ranges::subrange(first, last);
20        }
21
22        iterator& operator++() {
23            ++first;
24            ++last;
25            return *this;
26        }
27    };
28
29    auto begin() {
30        auto first = std::ranges::begin(base_);
31        auto last = first;
32        std::ranges::advance(last, std::min(k_, std::ranges::distance(first, std::ranges::end(base_))));
33        return iterator{first, last, std::ranges::end(base_), k_};
34    }
35
36    auto end() {
37        auto e = std::ranges::end(base_);
38        return iterator{e, e, e, k_};
39    }
40};
41
42template <std::ranges::viewable_range R>
43auto sliding_window(R&& r, size_t k) {
44    return sliding_window_view<std::ranges::views::all_t<R>>(std::views::all(std::forward<R>(r)), k);
45}

✅ 用法:

1std::vector<int> v{1, 3, -1, -3, 5, 3, 6, 7};
2
3for (auto w : sliding_window(v, 3)) {
4    int max = *std::ranges::max_element(w);
5    std::cout << max << " ";
6}
7// 输出: 3 3 5 5 6 7

🔗 2. 实现一个简版 zip_view

标准库暂未实现 views::zip,我们可以用结构绑定自定义一个。

💡 核心思想:

并行遍历两个或多个范围,组合成一个 tuple 或 pair。

🚧 简化 zip_view(仅支持两个 range)

 1template <std::ranges::input_range R1, std::ranges::input_range R2>
 2class zip_view {
 3    R1 r1_;
 4    R2 r2_;
 5
 6public:
 7    zip_view(R1 r1, R2 r2) : r1_(std::move(r1)), r2_(std::move(r2)) {}
 8
 9    struct iterator {
10        std::ranges::iterator_t<R1> it1;
11        std::ranges::iterator_t<R2> it2;
12
13        auto operator*() const {
14            return std::pair(*it1, *it2);
15        }
16
17        iterator& operator++() {
18            ++it1;
19            ++it2;
20            return *this;
21        }
22
23        bool operator!=(const iterator& other) const {
24            return it1 != other.it1 && it2 != other.it2;
25        }
26    };
27
28    iterator begin() {
29        return {std::ranges::begin(r1_), std::ranges::begin(r2_)};
30    }
31
32    iterator end() {
33        return {std::ranges::end(r1_), std::ranges::end(r2_)};
34    }
35};
36
37template <std::ranges::viewable_range R1, std::ranges::viewable_range R2>
38auto zip(R1&& r1, R2&& r2) {
39    return zip_view<std::ranges::views::all_t<R1>, std::ranges::views::all_t<R2>>(
40        std::views::all(std::forward<R1>(r1)), std::views::all(std::forward<R2>(r2)));
41}

✅ 用法:

1std::vector<int> a{1, 2, 3};
2std::vector<std::string> b{"one", "two", "three"};
3
4for (auto [x, y] : zip(a, b)) {
5    std::cout << x << ": " << y << "\n";
6}

🌈 3. 与协程结合的展望(C++20)

C++20 的协程与 ranges 是天然搭档。例如你可以编写 generator<T>

1generator<int> iota(int start, int end) {
2    for (int i = start; i < end; ++i)
3        co_yield i;
4}

然后包装为一个 view,实现懒惰无限序列、异步生成等。

目前标准库并未集成 coroutine-ranges,但 cppcoro, generator 等库已经实现了类似功能,未来 C++26 将会加强这种支持。


📊 4. 性能与对比分析

项目传统 STLranges 写法
可读性高(更像自然语言)
组合性高(管道链式)
惰性计算✅ 默认惰性
性能稍快与 STL 相近(懒惰 + inline)
Debug 类型简单❗可能复杂(需配合 decltype(auto)
拓展能力需手写✅ 可组合 views、自定义 view

✅ 推荐实践:在性能要求不极端的情况下,优先使用 ranges,写法更现代、语义更清晰。


📘 最终总结(完整版)

分类内容
ranges 算法ranges::sort, ranges::find, ranges::copy
viewsfilter, transform, take, drop, iota, reverse
链式组合`r
自定义 view可扩展行为(如滑窗、分组等)
zip_view / sliding_window实现复合逻辑
range concepts用于模板约束或接口泛化
协程结合可构建惰性数据流(未来趋势)
容器转换to<vector>()vector(r.begin(), r.end())
性能建议惰性默认行为,适合大数据惰性处理场景

最后修改于 2025-07-08 18:57