【问题标题】:Slicing a vector切片向量
【发布时间】:2020-04-14 17:53:03
【问题描述】:

我有一个 std::vector。我想创建代表该向量切片的迭代器。我该怎么做?在伪 C++ 中:

class InterestingType;

void doSomething(slice& s) {
    for (slice::iterator i = s.begin(); i != s.end(); ++i) {
       std::cout << *i << endl;
    }
}
int main() {
   std::vector v();
   for (int i= 0; i < 10; ++i) { v.push_back(i); }
   slice slice1 = slice(v, 1, 5);
   slice slice2 = slice(v, 2, 4);
   doSomething(slice1);
   doSomething(slice2);
   return 0;
}

我宁愿不必将元素复制到新的数据结构中。

【问题讨论】:

  • 为什么是这个社区维基?
  • 当您需要一个范围和一个不等于 1 的步幅时,通常将其称为“切片”。例如参见 std::gslice 和 std::valarray。步幅为 1 通常称为“范围”。

标签: c++ stl


【解决方案1】:

您只需使用一对迭代器:

typedef std::vector<int>::iterator vec_iter;

void doSomething(vec_iter first, vec_iter last) {
    for (vec_iter cur = first; cur != last; ++cur) {
       std::cout << *cur << endl;
    }
}

int main() {
   std::vector v();
   for (int i= 0; i < 10; ++i) { v.push_back(i); }

   doSomething(v.begin() + 1, v.begin() + 5);
   doSomething(v.begin() + 2, v.begin() + 4);
   return 0;
}

另外,Boost.Range 库应该允许您将迭代器对表示为单个对象,但以上是执行此操作的规范方法。

【讨论】:

  • 我会写“两个迭代器参数”iso迭代器对(std::pair
  • 这不适用于大于一的步幅(步数)。
【解决方案2】:

我在学习 C++ 之前学习了 Python。我想知道 C++ 是否提供了向量切片,​​例如 Python 列表中的切片。花了几分钟来编写这个函数,它允许您像在 Python 中那样对向量进行切片。

vector<int> slice(const vector<int>& v, int start=0, int end=-1) {
    int oldlen = v.size();
    int newlen;

    if (end == -1 or end >= oldlen){
        newlen = oldlen-start;
    } else {
        newlen = end-start;
    }

    vector<int> nv(newlen);

    for (int i=0; i<newlen; i++) {
        nv[i] = v[start+i];
    }
    return nv;
}

用法:

vector<int> newvector = slice(vector_variable, start_index, end_index);

start_index 元素将包含在切片中,而 end_index 将不包含在切片中。

示例:

对于像 {1,3,5,7,9} 这样的向量 v1

slice(v1,2,4) 返回 {5,7}

【讨论】:

  • Python 的切片还有一个可选的第三个参数,就像 range() 一样,用于 step。这就是为什么 x[::-1] 是一种快速简单的反转字符串或列表的方法。
  • 在大多数语言中,切片的美妙之处在于您可以在不进行复制操作的情况下获得子向量/字符串。您的示例逐个元素地复制数据。但是,如果您不关心分配或复制速度,那也没关系。
  • 不要使用这个功能。没有理由只为向量提供特殊功能;至少将其推广到任何容器。就像其他人建议的那样 - 传递一个范围(是的,我知道 C++ 还没有范围。但它很快就会出现,这个评论仍然会在这里。)
  • 在 c++ 中,您需要创建一个结构来保存切片的迭代器开始和结束。
【解决方案3】:

取自here

std::vector<myvector::value_type>(myvector.begin()+start, myvector.begin()+end).swap(myvector);

使用示例:

#include <iostream>
#include <vector>

int main ()
{
    std::vector<int> indexes{3, 6, 9};

    for( auto index : indexes )
    {
        int slice = 3;
        std::vector<int> bar{1, 2, 3, 4, 5, 6, 7, 8, 9};
        std::vector<int>( bar.begin() + index - slice, bar.begin() + index ).swap(bar);

        std::cout << "bar index " << index << " contains:";
        for (unsigned i=0; i<bar.size(); i++)
            std::cout << ' ' << bar[i];
        std::cout << '\n';
    }

    return 0;
}

输出:

bar index 3 contains: 1 2 3
bar index 6 contains: 4 5 6
bar index 9 contains: 7 8 9

【讨论】:

    【解决方案4】:

    正如其他人所说,您可以将“切片”表示为一对迭代器。如果您愿意使用 Boost,则可以使用范围概念。然后你甚至可以使用 begin()/end() 成员函数,整个东西看起来很像一个容器。

    【讨论】:

      【解决方案5】:

      使用升压范围适配器。他们是lazy:

      operator|() 用于懒惰地添加新行为,并且从不修改其 左参数。

      boost::for_each(v|sliced(1,5)|transformed(doSomething));
      

      doSomething 需要将范围作为输入。一个简单的(可能是 lambda)包装器可以解决这个问题。

      【讨论】:

        【解决方案6】:

        您可以使用一对迭代器来表示这些“切片”。

        【讨论】:

          【解决方案7】:

          您不需要一对迭代器来对向量进行切片。三个索引就可以了,因为它允许您通过步骤创建切片:

          static const int arr[] = {16,2,77,29,42};
          vector<int> v (arr, arr + sizeof(arr) / sizeof(arr[0]) );
          vector<int>::iterator i;
          const int step = 2;
          const int first = 0;
          const int last = v.size()-1;
          int counter=first;
          for (i = v.begin()+first; counter<last; i+=step, counter+=step) {
              // Do something with *i
              cout << *i << endl;
          }
          

          打印:

          16
          77
          

          在这段代码中,需要一个计数器来跟踪位置,因为不是所有的迭代器都能做到这一点。

          【讨论】:

            【解决方案8】:

            可以使用带有std::valarray 的切片。这是python中numpy.array的STL类似物。它支持不同的矢量化操作,如 min、max、+、-、*、/ 等。 更多信息here

            std::slice(start, length, stride) 允许在不复制的情况下选择和修改数组的切片(文档here)。

            切片看起来像这样:

              std::valarray<int> foo (9);
              for (int i=0; i<9; ++i) foo[i]=i;         //  0  1  2  3  4  5  6  7  8
                                                        //     |  |  |  |  |
              std::slice myslice=std::slice(1,5,1);     //     v  v  v  v  v
              foo[myslice] *= std::valarray<int>(10,3); //  0 10 20 30 40 50  6  7  8
            

            或者stride=2:

              std::valarray<int> foo (9);
              for (int i=0; i<9; ++i) foo[i]=i;         //  0  1  2  3  4  5  6  7  8
                                                        //     |     |     |
              std::slice myslice=std::slice(1,3,2);     //     v     v     v
              foo[myslice] *= std::valarray<int>(10,3); //  0 10  2 30  4 50  6  7  8
                                                        //  |        |        |
              foo[std::slice (0,3,3)] = 99;             //  v        v        v
                                                        // 99 10  2 99  4 50 99  7  8
              std::cout << "foo:";
              for (std::size_t n=0; n<foo.size(); n++)
                  std::cout << ' ' << foo[n];
              std::cout << '\n';
            

            【讨论】:

              猜你喜欢
              • 2014-06-10
              • 2018-07-10
              • 1970-01-01
              • 1970-01-01
              • 2015-07-05
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多