将范围拆分为子范围

发布于 2024-10-01 19:58:10 字数 842 浏览 5 评论 0原文

我有一个容器 std::vector,我想将其有效地分成子范围,每个子范围包含 x 项。不需要原始容器,因此应移动项目而不是将其复制到子范围中。

我已经设法使用复制来进行分割,但是我不确定如何通过移动分配来进行分割?

    range.insert(range.end(), new_items.begin(), new_items.end());
    while(range.size() >= x)
    {
        sub_ranges.push_back(std::vector<int>(range.begin(), range.begin() + x));
        range = std::vector<int>(range.begin() + x, range.end());
    }

编辑:

一些进展......仍然没有完全实现,而且有点难看

    while(range.size() >= x)
    {
        std::vector<short> sub_range(x); // Unnecessary allocation?
        std::move(range.begin(), range.begin() + x, sub_range.begin());
        sub_ranges_.push_back(std::move(sub_range));

        std::move(range.begin() + x, range.end(), range.begin());
        range.resize(range.size() - x);
    }

I have a container std::vector and I would like to efficiently split it into sub-ranges with x items in each. The original container is not needed so the items should be moved and not copied into the sub-ranges.

I've managed to do the splitting using copying, however I'm unsure how to do it with move assignments?

    range.insert(range.end(), new_items.begin(), new_items.end());
    while(range.size() >= x)
    {
        sub_ranges.push_back(std::vector<int>(range.begin(), range.begin() + x));
        range = std::vector<int>(range.begin() + x, range.end());
    }

EDIT:

Some progress... still not quite there, and a bit ugly

    while(range.size() >= x)
    {
        std::vector<short> sub_range(x); // Unnecessary allocation?
        std::move(range.begin(), range.begin() + x, sub_range.begin());
        sub_ranges_.push_back(std::move(sub_range));

        std::move(range.begin() + x, range.end(), range.begin());
        range.resize(range.size() - x);
    }

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

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

发布评论

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

评论(2

无妨# 2024-10-08 19:58:11

有一个问题:您听说过 View 概念吗?

这个想法是,您只需创建一个“视图”(代理模式)来限制/改变您对数据的看法,而不是实际移动数据。

例如,对于范围,一个非常简单的实现是:

template <typename Iterator>
struct Range
{
  Iterator mBegin, mEnd;
};

Boost.Range 提供了一个胖版本,包含很多东西。

在这种情况下,优点有很多。其中:

  • 单个向量,因此具有更好的内存局部性
  • 分割很简单,并且不涉及数据的任何移动/复制

以下是使用此方法的分割

typedef Range<std::vector<int>::iterator> range_type;

std::vector<range_type> split(std::vector<int> const& range, size_t x)
{
  std::vector<range_type> subRanges;
  for (std::vector<int>::iterator it = range.begin(), end = range.end();
       it != end; it += std::min(x, (size_t)std::distance(it,end)))
  {
    subRanges.push_back(range_type(it,end));
  }
  return subRanges;
}

当然,只有当您可以保留 range 对象时,这才有效。


关于您的原始算法:这里使用 while 循环是虚假的,并迫使您使用比必要的更多的 move 。我制作的 for 循环在这方面应该要好得多。

One question: have you ever heard of the View concept.

The idea is that instead of actually moving the data, you just create a "view" (Proxy pattern) that will restrict / change your perception of it.

For example, for a range, a very simple implementation would be:

template <typename Iterator>
struct Range
{
  Iterator mBegin, mEnd;
};

Boost.Range offers a fat version, with a lot of things.

The advantages are numerous in this case. among which:

  • A single vector, thus better memory locality
  • The split is easy, and doesn't involve any move / copy of the data

Here is the split with this method:

typedef Range<std::vector<int>::iterator> range_type;

std::vector<range_type> split(std::vector<int> const& range, size_t x)
{
  std::vector<range_type> subRanges;
  for (std::vector<int>::iterator it = range.begin(), end = range.end();
       it != end; it += std::min(x, (size_t)std::distance(it,end)))
  {
    subRanges.push_back(range_type(it,end));
  }
  return subRanges;
}

Of course, this only works if you can keep the range object around.


Regarding your original algorithm: the use of a while loop is spurious here, and forces you to use much more moves than necessary. The for loop I crafted should be much better in this regard.

逆夏时光 2024-10-08 19:58:11

您可以在 中使用 std::make_move_iterator 将迭代器包装到 move_iterator 中。该迭代器将 std::move 取消引用其基本迭代器的结果,从而允许将其移动到其他地方。

// assuming I understand your code, which I don't
range.insert(range.end(), new_items.begin(), new_items.end());
while(range.size() >= x)
{
    auto first = std::make_move_iterator(range.begin());
    auto last = std::make_move_iterator(range.begin() + x);

    sub_ranges.push_back(std::vector<int>(first, last));
    range = std::vector<int>(range.begin() + x, range.end());
}

编辑:就像您发现的那样,std::move()make_move_iterator 之间存在映射:

std::move(first, last, out); // is the same as
std::copy(std::make_move_iterator(first), std::make_move_iterator(last), out);

因此您发现哪个更干净取决于您。 (第一个,对我来说。)

You can use std::make_move_iterator in <iterator> to wrap your iterator into a move_iterator. This iterator will std::move the result of dereferencing its base iterator, allowing that to be moved elsewhere.

// assuming I understand your code, which I don't
range.insert(range.end(), new_items.begin(), new_items.end());
while(range.size() >= x)
{
    auto first = std::make_move_iterator(range.begin());
    auto last = std::make_move_iterator(range.begin() + x);

    sub_ranges.push_back(std::vector<int>(first, last));
    range = std::vector<int>(range.begin() + x, range.end());
}

EDIT: And like you found, there's a mapping between std::move() and make_move_iterator:

std::move(first, last, out); // is the same as
std::copy(std::make_move_iterator(first), std::make_move_iterator(last), out);

So which you find cleaner is up to you. (The first one, to me.)

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