使用交替模式合并两个 STL 向量

发布于 2024-09-17 21:06:03 字数 623 浏览 2 评论 0原文

我有两个 STL 向量 A 和 B,需要将它们合并到第三个向量中,其中元素应该以某种方式排序,输出向量中的每个第 n 个元素都应该是向量 B。我当前的代码如下所示:

std::vector<int> a(10, 4);
std::vector<int> b(10, 8);
std::vector<int> c;
static const std::size_t STEP(3);

std::vector<int>::const_iterator bIt = b.begin();
for(std::vector<int>::const_iterator aIt = a.begin();
    aIt != a.end(); ++aIt)
{
    c.push_back(*aIt);
    if((c.size() + 1) % STEP == 0)
    {
        c.push_back(*bIt);
        ++bIt; //assume b is large enough
    }
}

向量 c 现在看起来像: 4 4 8 4 4 8 ...

这工作正常,但我很好奇是否没有更优雅的解决方案。有没有办法使用STL算法代替我手写的循环?

I have two STL vectors A and B and need to merge them into a third one, where the elements should be ordered in a way, that every nth element in the output vector should be of vector B. My current code looks something like this:

std::vector<int> a(10, 4);
std::vector<int> b(10, 8);
std::vector<int> c;
static const std::size_t STEP(3);

std::vector<int>::const_iterator bIt = b.begin();
for(std::vector<int>::const_iterator aIt = a.begin();
    aIt != a.end(); ++aIt)
{
    c.push_back(*aIt);
    if((c.size() + 1) % STEP == 0)
    {
        c.push_back(*bIt);
        ++bIt; //assume b is large enough
    }
}

The vector c now looks like:
4 4 8 4 4 8 ...

This works fine, but I'm curious if there isn't a more elegant solution. Is there any way use a STL algorithm instead of my hand-written loops?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

幽蝶幻影 2024-09-24 21:06:03

我必须承认我非常喜欢 Potatoswatter 解决方案......非常喜欢。

正如他所证明的,这不是算法的问题,而是迭代的问题。然而,他的解决方案不太符合要求,因为测试迭代的结束非常困难:在准备容器和创建迭代器时需要非常小心,以避免未定义的行为。

我认为使用与迭代器非常相似的概念可以更好地表达这个问题:视图。

视图是一个或多个容器上的适配器接口。它本身实际上并不包含任何东西(这是重要的部分)。然而,它确实公开了一个类似于容器的接口,以便您可以使用常用的习惯进行编码。

视图的美妙之处在于您可以经常组合它们。

例如,一个非常简单的视图:

/// Only allow to see a range of the container:
/// std::vector<int> v(40, 3); // { 3, 3, 3, ... }
/// auto rv = make_range_view(v, 4, 5);
/// rv exposes the elements in the range [4,9)
/// in debug mode, asserts that the range is sufficiently large
template <typename Container>
class range_view
{
};

对于您的问题,您宁愿创建一个 interleave_view

/// Allow to interleave elements of 2 containers each at its own pace
/// std::vector<int> v(40, 3);
/// std::set<int> s = /**/;
/// 
/// auto iv = make_interleave_view(v, 3, s, 1);
/// Takes 3 elements from v, then 1 from s, then 3 from v, etc...
template <typename C1, typename C2>
class interleave_view
{
};

这里结束迭代器的问题在某种程度上得到了缓解,因为我们知道两个容器,因此我们能够创建迭代器我们自己,确保我们传递正确的参数。

当然,如果容器不提供有效的“大小”成员(list 不提供,它的时间复杂度为 O(n)),它可能会变得更加乏味。

然而,一般原则是视图为您提供更多控制(并允许更多检查),并且使用起来更安全,因为您可以精确控制迭代器的创建。

请注意,在 C++0x 中,interleave_view 通常可以容纳无限数量的序列。

I must admit I quite like Potatoswatter solution... quite.

As he demonstrated, this is not an issue of algorithm, but of iteration. However his solution does not quite fit the bill because testing for the end of the iteration is very difficult: it requires much care in the preparation of the containers and the creation of the iterators to avoid undefined behavior.

I think the problem could be much better expressed using a concept that is quite similar to iterators: views.

A view is an Adapter interface over one or several containers. It doesn't actually contain anything itself (that's the important part). It does however exposes an interface similar to that of a container so that you can code with the usual idioms.

The beautiful things about views, is that you can often compose them.

For example, one very simple view:

/// Only allow to see a range of the container:
/// std::vector<int> v(40, 3); // { 3, 3, 3, ... }
/// auto rv = make_range_view(v, 4, 5);
/// rv exposes the elements in the range [4,9)
/// in debug mode, asserts that the range is sufficiently large
template <typename Container>
class range_view
{
};

For your question, you would rather create an interleave_view:

/// Allow to interleave elements of 2 containers each at its own pace
/// std::vector<int> v(40, 3);
/// std::set<int> s = /**/;
/// 
/// auto iv = make_interleave_view(v, 3, s, 1);
/// Takes 3 elements from v, then 1 from s, then 3 from v, etc...
template <typename C1, typename C2>
class interleave_view
{
};

Here the issue of the end iterator is somehow alleviated because we know both containers and thus we are able to create the iterators ourselves, ensuring we pass the proper parameters.

Of course it can become a bit more tedious if the containers do not offer an efficient "size" member (list doesn't, it's O(n)).

The general principle however is that a view gives you more control (and allows more checks), and is safer to use because you control precisely the creation of the iterators.

Note that in C++0x the interleave_view would typically accomodate an unbounded number of sequences.

孤独岁月 2024-09-24 21:06:03

这太专业了,无法直接由 涵盖。避免循环将需要自定义迭代器。

template< typename I1, typename I2 >
struct interleave_iterator
    : std::iterator< forward_iterator_tag, typename I1::value_type > {
    using typename I1::value_type;

    I1 i1;
    I2 i2;
    size_t cnt, stride;

    interleave_iterator( I1 in1, I2 in2, size_t in_stride=0, size_t in_off=0 )
        : i1( in1 ), i2( in2 ), cnt( in_off ), stride( in_stride ) {}

    value_type &operator*() const { return cnt? * i1 : * i2; }
    interleave_iterator &operator++() {
        if ( ++ cnt == stride ) {
            cnt = 0;
            ++ i2;
        } else ++ i1;
        return *this;
    }
    value_type *operator->() const
        { return cnt? i1.operator->() : i2.operator->(); }

    interleave_iterator &operator++(int)
        { interleave_iterator r = *this; ++ *this; return r; }

    friend bool operator==
        ( interleave_iterator const &lhs, interleave_iterator const &rhs )
        { return lhs.i1 == rhs.i1 && lhs.i2 == rhs.i2; }
    friend bool operator!=
        ( interleave_iterator const &lhs, interleave_iterator const &rhs )
        { return ! ( lhs == rhs ); }
};

我觉得有点过分了。

This is too specialized to be covered directly by <algorithm>. Avoiding the loop will require a custom iterator.

template< typename I1, typename I2 >
struct interleave_iterator
    : std::iterator< forward_iterator_tag, typename I1::value_type > {
    using typename I1::value_type;

    I1 i1;
    I2 i2;
    size_t cnt, stride;

    interleave_iterator( I1 in1, I2 in2, size_t in_stride=0, size_t in_off=0 )
        : i1( in1 ), i2( in2 ), cnt( in_off ), stride( in_stride ) {}

    value_type &operator*() const { return cnt? * i1 : * i2; }
    interleave_iterator &operator++() {
        if ( ++ cnt == stride ) {
            cnt = 0;
            ++ i2;
        } else ++ i1;
        return *this;
    }
    value_type *operator->() const
        { return cnt? i1.operator->() : i2.operator->(); }

    interleave_iterator &operator++(int)
        { interleave_iterator r = *this; ++ *this; return r; }

    friend bool operator==
        ( interleave_iterator const &lhs, interleave_iterator const &rhs )
        { return lhs.i1 == rhs.i1 && lhs.i2 == rhs.i2; }
    friend bool operator!=
        ( interleave_iterator const &lhs, interleave_iterator const &rhs )
        { return ! ( lhs == rhs ); }
};

A little excessive, I think.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文