首页 > 解决方案 > 在 range-v3 管道中插入一系列自定义元素的正确方法是什么?

问题描述

假设(这只是一个例子)我想使用 range-v3 库来创建一个序列,比如这个:

2 3 7 20 30 70 200 300 700 2000 3000 7000 ...

基本上,对于i我从我那里获得的每一个,iota(0)我都想将一个序列2*10^i, 3*10^i, 7*10^i插入到管道中以进行进一步处理:

#include <cmath>
#include <iostream>
#include <range/v3/all.hpp>

int main(){
    using namespace ranges::views;

    auto rng = iota(0) |
        /*
            insert 2*10^i, 3*10^i, 7*10^i
        */ |
        take_while([](int x){ return x < 10000;});

    for(auto i: rng) {
        std::cout << i << " ";
    }
    std::cout << "\n";
}

我不确定如何正确实施。我设法通过返回临时容器来创建一个工作示例,如本答案中所述:

#include <cmath>
#include <iostream>
#include <range/v3/all.hpp>

int main(){
    using namespace ranges::views;

    auto rng = iota(0) |
               transform([](int i) {
                   int mul = pow(10, i);
                   return std::array{2*mul, 3*mul, 7*mul};
               }) |
               cache1 |
               join |
               take_while([](int x){ return x < 10000;});

    for(auto i: rng) {
        std::cout << i << " ";
    }
    std::cout << "\n";
}

但我想知道我是否可以更直接地做到这一点。事实上,ranges::views::for_each听起来很合适(因为它会自动展平返回的范围),但我不确定要从中返回什么:

auto rng = iota(0) |
    for_each([](int i){
        int mul = pow(10, i);
        return /* ?????????????????????????? */
    }) | 
    take_while([](int x){ return x < 10000;});

或者也许有更惯用的方式在管道中插入自定义元素?

标签: c++range-v3

解决方案


我的一个想法是编写自己的视图,将传入的数据存储在std::initializer_listprivate中std::vector

#include <vector>

template <typename T>
class store_view: public ranges::view_facade<store_view<T>> {
    friend ranges::range_access;
    std::vector<T> data_;
    size_t pos_;

    T const & read() const { return data_[pos_]; }
    bool equal(ranges::default_sentinel_t) const { return data_.size() == pos_; }
    void next() { ++pos_; }
public:
    store_view() = default;
    explicit store_view(std::initializer_list<T> data): data_{data}, pos_{0} {
    }
};

在管道中使用这个视图看起来很整洁:

auto rng = iota(0) |
    for_each([](int i){
        int mul = pow(10, i);
        return store_view{2*mul, 3*mul, 7*mul};
    }) | 
    take_while([](int x){ return x < 10000;});

它有效,但似乎代价高昂,因为它为流中的每个值创建了一个新向量。一种不太漂亮但更有效的方法是只创建一次包含,然后在 lambda 中通过引用捕获它(因为它需要比它更长寿),更新它并使用以下方法作为视图返回ranges::views::all

std::array<int, 3> values;
auto rng = iota(0) |
    for_each([&values](int i){
        int mul = pow(10, i);
        values[0] = 2*mul;
        values[1] = 3*mul;
        values[2] = 7*mul;
        return values | all;
    }) | 
    take_while([](int x){ return x < 10000;}); 

仍然不确定是否有更好的方法。


推荐阅读