哪个 STL 容器具有切片成两个或多个容器的设施

Which STL container has facilities for slicing into two or more containers?

本文关键字:两个 设施 STL 切片 哪个      更新时间:2023-10-16

假设我有一个向量/列出填充了 2300 个值
的任何整数我希望能够轻松地将其切成 4 个向量/列表(不一定大小相等)。
例如

vec1 ( elements 0 - 500 )
vec2 ( elements 501 - 999)
vec3 ( elements 1001 - 1499)


等。

一种常见的方法是使用一个容器,并在其上定义单独的迭代器范围。

std::vector<int> vec(2300);
it0 = vec.begin();
it1 = vec.begin() + 500;
it2 = vec.begin() + 1000;
it3 = vec.begin() + 1500;
it4 = vec.begin() + 2000;
it5 = vec.end();

现在,第一个范围仅由迭代器it0it1定义。第二个由it1it2,依此类推。

因此,如果要将函数应用于第三个范围中的每个元素,只需执行以下操作:

std::for_each(it2, it3, somefunc);
实际上,将

元素复制到单独的容器中可能是不必要的,并且会带来性能成本。

std::list将是最佳选择,因为您只需通过连接指针来构建列表。但是,找到切片的确切位置将是问题所在,因为您必须到达列表迭代器中的该点才能进行切割。

编辑

根据评论(感谢您的见解),也许使用 std::vector<int> 和迭代器是个好主意。但是,使用普通迭代器,您会丢失向量的长度,因此我建议使用例如boost::range_iterator

std::vector<int> vec(2300);
it0 = vec.begin();
it1 = vec.begin() + 500;
it2 = vec.begin() + 1000;
it3 = vec.begin() + 1500;
it4 = vec.begin() + 2000;
it5 = vec.end;
typedef boost::iterator_range< std::vector<int>::iterator > my_slice_t;
my_slice_t slice1 = boost::make_iterator_range(it0, it1);
...

然后,您可以将 slice1 用作每个迭代的普通基础std::vector<int>

std::for_each(slice1.begin(), slice1.end(), /* stuff */);

请参阅此处记录的第四个std::vector<>构造函数。

// given std::vector<T> vec with 2300 elements
std::vector<T> vec1(vec.begin(), vec.begin() + 500);
std::vector<T> vec2(vec.begin() + 500, vec.begin() + 1000);
std::vector<T> vec3(vec.begin() + 1000, vec.begin() + 1500);
std::vector<T> vec4(vec.begin() + 1500, vec.begin() + 2000);
std::vector<T> vec5(vec.begin() + 2000, vec.end());
实际上,

使用向量容器是可行的

#include <vector>
#include <iostream>
using namespace std ;
int main()
{
  vector<int> ints ;
  vector<int> ints_sliced;
  int i ;
  // populate
  for( i = 0 ; i < 100 ; i++ ) 
    ints.push_back(i) ;
  // slice from 10-19
  ints_sliced.insert(ints_sliced.begin(), ints.begin()+10, ints.begin()+20) ;
  // inspect
  vector<int>::iterator it ;
  for( it = ints_sliced.begin() ; it != ints_sliced.end() ; it++ )
    cout << *it << endl ;

}

哦,如果你碰巧使用 g++ 和 GNU stdlibc++,你可以大致使用

g++ -march=native -O3 -ftree-vectorize ...

如果你也加入GNU OpenMP支持(libgomp),你可以从标准算法的自动并行化中受益(评估,分析!)。

g++ -D_GLIBCXX_PARALLEL -fopenmp -march=native -O3 .... -lgomp

YMMV - 我想把它扔在那里,因为例如并行for_each似乎接近你想要的(但是,自动魔术和自适应容器大小、迭代器类型和处理器数量)

除了@jalf的正确观察之外,实际上将子向量复制到新的向量中可能是浪费时间和空间,让我指出 valarray。

简介:瓦拉阵列

Valarray 可能更复杂,但尤其是在面对并行处理时,可能会导致更好的方法来为不同的线程进行子向量工作。 要寻找的东西:

  1. 算法预科学(如果某个模式中的位置具有某种属性(例如已知为零),您可以将其交给优化的工人以获取这些值)
  2. 子向量对齐(这种一致性可以决定 SIMD、SSE4 优化版本的可用性;请查看 gcc -ftree-vectorizer 了解更多背景信息)

现在 valarrays 有很多"晦涩难懂"的操作和技巧(gslices;基本上是重新矢量化的数组维度以解决原始数组),我不会在这里讨论,但足以说,如果你想在(主要是)浮点[1]的连续数组的子集之间进行数字运算,阅读这些是值得的。

强制性(脑死亡)预告片

// mask_array example
#include <iostream>
#include <valarray>
using namespace std;
int main ()
{
  valarray<int> myarray (10);
  for (int i=0; i<10; ++i) myarray[i]=i;   //  0  1  2  3  4  5  6  7  8  9
  valarray<bool> mymask (10);
  for (int i=0; i<10; ++i)
    mymask[i]= ((i%2)==1);                 //  f  t  f  t  f  t  f  t  f  t
  myarray[mymask] *= valarray<int>(10,5);  //  0 10  2 30  4 50  6 70  8 90
  myarray[!mymask] = 0;                    //  0 10  0 30  0 50  0 70  0 90
  cout << "myarray:n";
  for (size_t i=0; i<myarray.size(); ++i)
      cout << myarray[i] << ' ';
  cout << endl;
  return 0;
}

这是从上面的链接逐字复制的,您将需要适应您的特定需求。你可能有一个很好的理由让你的最终目标有点模糊,所以我很乐意把剩下的工作留给你!

总结

然而,如果你真的想一路走下去,你应该开始看看大枪(Blitz++等)。

[1] 这些历来是矢量化 CPU 指令集的重点。但是,als @jalf 注释、SSE2 及更高版本也包括 SIMD 整数指令