【问题标题】:Slicing a vector in C++在 C++ 中对向量进行切片
【发布时间】:2018-11-06 01:23:44
【问题描述】:

C++ 中是否有相当于列表切片[1:] 的C++ 向量?我只是想从向量中获取除第一个元素之外的所有元素。

Python 的列表切片操作符:

list1 = [1, 2, 3]
list2 = list1[1:]  

print(list2) # [2, 3]

C++ 期望的结果:

std::vector<int> v1 = {1, 2, 3};
std::vector<int> v2;
v2 = v1[1:];

std::cout << v2 << std::endl;  //{2, 3}

【问题讨论】:

  • v2 = std::vector&lt;int&gt;(v1.begin() + 1, v1.end());
  • @DimChtz -- 将其作为答案。
  • @juanchopanza 我会在帖子里解释更多
  • 要复制还是引用元素?
  • 这能回答你的问题吗? Best way to extract a subvector from a vector?

标签: python c++ vector slice


【解决方案1】:

这可以使用std::vector 的复制构造函数轻松完成:

v2 = std::vector<int>(v1.begin() + 1, v1.end());

正如@AlessandroFlati 建议的那样,我应该澄清一下,这将包括 v1.end()

【讨论】:

  • 这个答案可以改进提及是否包含v1.end()
  • @AlessandroFlati 你能解释一下你的意思吗?
  • @AlessandroFlati 是的,确实如此。否则我的回答将是错误的,因为 OP 询问:“我只是想从向量中获取除第一个元素之外的所有元素”:)
  • 我知道确实如此,我只是建议将其包含在答案中以使其完美无缺。
  • 只是为了记录,v1.end()不是向量末尾的元素,它是指向(不存在的)元素的迭代器之后最后一个元素。
【解决方案2】:

在 C++20 中这很简单:

#include <span>
#include <vector>
#include <iostream>

template<int left = 0, int right = 0, typename T>
constexpr auto slice(T&& container)
{
    if constexpr (right > 0)
    {
        return std::span(begin(std::forward<T>(container))+left, begin(std::forward<T>(container))+right);
    }
    else
    {
        return std::span(begin(std::forward<T>(container))+left, end(std::forward<T>(container))+right);
    }
}



int main()
{
    std::vector v{1,2,3,4,5,6,7,8,9};

    std::cout << "-------------------" << std::endl;
    auto v0 = slice<1,0>(v);
    for (auto i : v0)
    {
        std::cout << i << std::endl;
    }

    std::cout << "-------------------" << std::endl;
    auto v1 = slice<0,-1>(v);
    for (auto i : v1)
    {
        std::cout << i << std::endl;
    }

    std::cout << "-------------------" << std::endl;
    auto v2 = slice<1,3>(v);
    for (auto i : v2)
    {
        std::cout << i << std::endl;
    }

    std::cout << "-------------------" << std::endl;
    auto v3 = slice<1,-1>(v);
    for (auto i : v3)
    {
        std::cout << i << std::endl;
    }

    std::cout << "-------------------" << std::endl;
    auto v4 = slice<3,3>(v);
    for (auto i : v4)
    {
        std::cout << i << std::endl;
    }

}

结果:

Program returned: 0
-------------------
2
3
4
5
6
7
8
9
-------------------
1
2
3
4
5
6
7
8
-------------------
2
3
-------------------
2
3
4
5
6
7
8
-------------------

您还可以添加边界检查和其他情况,例如负左索引等...但这只是一个示例。

在编译器资源管理器中运行:https://godbolt.org/z/qeaxvjdbj

【讨论】:

  • 这个切片语法很酷。但是为什么不使用std::slice呢?
  • std::slice 没问题,但我正在尝试类似于 std::vector 的 python 语法。据我了解,std::slice 不适用于 std::vector。 (之前的评论被截断了)
【解决方案3】:

我知道已经很晚了,但请查看valarray 及其slices。如果您使用某种NumericType 的向量,那么值得一试。

【讨论】:

    【解决方案4】:

    这取决于您是想要视图还是副本。

    Python 对列表的切片会复制对元素的引用,因此不能简单地将其视为视图或副本。例如,

    list1 = [1, 2, 3]
    list2 = list1[1:]  
    list2[1] = 5
    print(list1) # does not change, still [1, 2, 3]
    
    list1 = [1, 2, [3]]
    list2 = list1[1:]  
    list2[1][0] = 5
    print(list1) # changes, becomes [1, 2, [5]]
    

    详情请见this post

    DimChtz's anwer 模拟复制情况。如果你只是想要一个视图,在 C++20 中,你可以使用ranges(除了std::vies::dropstd::views::takestd::views::counted 也很有用):

    auto v2 = v1 | std::views::drop(1); // #include <ranges>
    for (auto &e: v2) std::cout << e << '\n';  
    

    std::span:

    std::span v2{v1.begin() + 1, v1.end()}; // #include <span>
    for (auto &e: v2) std::cout << e << '\n';  
    

    【讨论】:

      【解决方案5】:

      您可以按照上面的答案。了解多种方式总是更好。

      int main
      {
          std::vector<int> v1 = {1, 2, 3};
          std::vector<int> v2{v1};
          v2.erase( v2.begin() );
          return 0;
      }
      

      【讨论】:

      • 我会说这种方式与上面的解决方案相比效率低下,因为erase 将删除第一个元素,然后重新定位所有其他元素。那么你首先执行 N 个元素的副本,然后删除一个,然后移动 N-1 个。
      • 我从来没有说过它是有效的。我将其作为另一种方式分享。
      【解决方案6】:

      似乎最便宜的方法是使用指向起始元素的指针和切片中的元素数量。它不会是一个真正的向量,但对于许多用途来说已经足够了。

      【讨论】:

        猜你喜欢
        • 2016-10-01
        • 1970-01-01
        • 2010-12-17
        • 1970-01-01
        • 1970-01-01
        • 2012-11-20
        • 2019-01-12
        • 1970-01-01
        相关资源
        最近更新 更多