Slicing a vector in C++ - c++

Is there an equivalent of list slicing [1:] from Python in C++ with vectors? I simply want to get all but the first element from a vector.
Python's list slicing operator:
list1 = [1, 2, 3]
list2 = list1[1:]
print(list2) # [2, 3]
C++ Desired result:
std::vector<int> v1 = {1, 2, 3};
std::vector<int> v2;
v2 = v1[1:];
std::cout << v2 << std::endl; //{2, 3}

This can easily be done using std::vector's copy constructor:
v2 = std::vector<int>(v1.begin() + 1, v1.end());

In C++20 it is pretty easy:
#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;
}
}
Result:
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
-------------------
You can also add boundary checks and other cases like negative left indices etc... but this is only an example.
Run in compiler explorer: https://godbolt.org/z/qeaxvjdbj

I know it's late but have a look at valarray and its slices. If you are using a vector of some sort of NumericType, then it's worth giving it a try.

It depends on whether you want a view or a copy.
Python's slicing for lists copies references to the elements, so it cannot be simply regarded as a view or a copy. For example,
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]]
See this post for details.
DimChtz's anwer models the copy situation. If you just want a view, in C++20, you can use ranges (besides std::views::drop, std::views::take and std::views::counted are also useful):
auto v2 = v1 | std::views::drop(1); // #include <ranges>
for (auto &e: v2) std::cout << e << '\n';
or std::span:
std::span v2{v1.begin() + 1, v1.end()}; // #include <span>
for (auto &e: v2) std::cout << e << '\n';

You can follow the above answer. It's always better to know multiple ways.
int main
{
std::vector<int> v1 = {1, 2, 3};
std::vector<int> v2{v1};
v2.erase( v2.begin() );
return 0;
}

It seems that the cheapest way is to use pointer to the starting element and the number of elements in the slice. It would not be a real vector but it will be good enough for many uses.

Related

How to lexicographically compare two vectors in reverse order?

If I want to compare two vectors in lexicographical order, I can do as follows:
int main() {
std::vector<int> a{0, 7, 8, 9};
std::vector<int> b{1, 2, 3, 4};
std::cout << std::boolalpha;
std::cout << "a < b returns " << (a < b) << '\n';
}
But doing the same in reverse order fails to compile:
int main() {
std::vector<int> a{3, 2, 1};
std::vector<int> b{9, 8, 7, 6};
std::cout << std::boolalpha;
std::cout << "revrese a < reverse b returns " << ((a | std::views::reverse) < (b | std::views::reverse)) << '\n';
}
The latter code fails with:
<source>:23:81: error: no match for 'operator<' (operand types are 'std::ranges::reverse_view<std::ranges::ref_view<std::vector<int> > >' and 'std::ranges::reverse_view<std::ranges::ref_view<std::vector<int> > >')
23 | std::cout << "reverse a < reverse b returns " << ((a | std::views::reverse) < (b | std::views::reverse)) << '\n';
| ~~~~~~~~~~~~~~~~~~~~~~~~~ ^ ~~~~~~~~~~~~~~~~~~~~~~~~~
| | |
| reverse_view<[...]> reverse_view<[...]>
So, how to do achieve this properly?
I needed this because, I was creating a custom big integer class. In that, I was storing nodes in little endian order. Now, to compare if two integers are equal, first I compare their sizes. Then I would need to compare equal sized vectors in lexicographical order of their reverse view.
operator< is not defined for std::views::reverse and others. There is however a normal algorithm as normal function template for it in the standard library: std::lexicographical_compare with iterator interface and std::ranges::lexicographical_compare with range interface.
std::cout << "revrese a < reverse b returns "
<< std::ranges::lexicographical_compare(a | std::views::reverse, b | std::views::reverse)
<< '\n';
As #user17732522 said you can use std::lexicographical_compare && std::views::reverse but if you want use c++11 you can simply use reverse iterators with std::lexicographical_compare.
#include <algorithm>
#include <iostream>
#include <vector>
int main() {
std::vector<int> a{0, 7, 8, 9};
std::vector<int> b{1, 2, 3, 4};
std::cout << std::boolalpha;
std::cout << "a < b returns "
<< std::lexicographical_compare(a.cbegin(), a.cend(), b.rbegin(),
b.rend())
<< '\n';
}

Create shared objects in container using STL

I'm trying to generate a container with shared objects.
Say, two linked list with a couple of common nodes.
L1 : {1 2 3 2 1} and L2 : {2 1}
I know if we use the Node* head and add the dynamically created objects as link, that's possible to chain common objects in both linkedlist, but is it possible to create such shared objects directly in the container ?
I was trying something like this:
std::forward_list<int> listOne{1,2,3,2,1}; // 2,1 are meant to be shared
std::forward_list<int> listTwo{1};
listTwo.insert_after(listTwo.begin(),
std::next(listOne.begin(), 3), listOne.end()); // taking 2,1 from listOne
But, that would just create another new allocation.
I'm assuming this might be prohibited due to iterator invalidation scenarios etc. producing abnormal behaviour, but I'm not sure. Is it possible any other way ?
You can use std::shared_ptr as that will keep its objects alive regardless if another std::shared_ptr pointing to the same object is deleted from a different container.
The container type isn't important for that, so you can use std::vector for example:
std::vector<std::shared_ptr<int>> v1
{
std::make_shared<int>(1),
std::make_shared<int>(2),
std::make_shared<int>(3),
std::make_shared<int>(2),
std::make_shared<int>(1),
};
std::vector<std::shared_ptr<int>> v2
{
v1[3],
v1[4],
};
std::cout << "v1" << '\n';
for(auto& sp: v1)
std::cout << *sp << '\n';
std::cout << "v2" << '\n';
for(auto& sp: v2)
std::cout << *sp << '\n';
v1.pop_back(); // remove from one container but
v1.pop_back(); // still alive and kicking in the other
std::cout << "----" << '\n';
std::cout << "v1" << '\n';
for(auto& sp: v1)
std::cout << *sp << '\n';
std::cout << "v2" << '\n';
for(auto& sp: v2)
std::cout << *sp << '\n';
Output:
v1
1
2
3
2
1
v2
2
1
----
v1
1
2
3
v2
2
1
You can create a list of shared pointers for this.
std::list<int> listInt {1,2,3,2,1};
std::list<std::shared_ptr<int>> listOne{};
std::list<std::shared_ptr<int>> listTwo{};
size_t counter = 0;
for(int elem : listInt)
{
auto ptr1 = std::make_shared<int>(elem);
listOne.push_back(ptr1);
if(counter < 3)
listTwo.push_back(std::make_shared<int>(elem));
else
listTwo.push_back(ptr1);
counter++;
}

std::search to find a rightmost subsequence

I am trying to find a rightmost subsequence in a range with std::search and std::make_reverse_iterator.
However the iterator returned always point to the start of a range. What am I doing wrong?
TEST(basic_test, find_from_right)
{
std::vector<uint8_t> array{0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
std::array<uint8_t, 2> subSeq{3, 4};
auto found = std::search(std::make_reverse_iterator(array.cend()),
std::make_reverse_iterator(array.cbegin()),
subSeq.cbegin(),
subSeq.cend());
// makes no difference
// std::make_reverse_iterator(subSeq.cend()),
// std::make_reverse_iterator(subSeq.cbegin()));
auto distance = std::distance(found.base(), array.cbegin());
EXPECT_EQ(distance, 3);
}
Output:
Failure
Expected equality of these values:
distance
Which is: 0
3
I have a function that is provided with 2 template RandomIterators, so I have to call std::make_reverse_iterator. These containers are just for the sake of reproducing the problem and compiling the example.
I think this could solve your problem (if you are okay with C++17):
From https://en.cppreference.com/w/cpp/algorithm/find_end:
#include <algorithm>
#include <iostream>
#include <vector>
int main()
{
std::vector<int> v{1, 2, 3, 4, 1, 2, 3, 4, 1, 2, 3, 4};
std::vector<int>::iterator result;
std::vector<int> t1{1, 2, 3};
result = std::find_end(v.begin(), v.end(), t1.begin(), t1.end());
if (result == v.end()) {
std::cout << "sequence not found\n";
} else {
std::cout << "last occurrence is at: "
<< std::distance(v.begin(), result) << "\n";
}
std::vector<int> t2{4, 5, 6};
result = std::find_end(v.begin(), v.end(), t2.begin(), t2.end());
if (result == v.end()) {
std::cout << "sequence not found\n";
} else {
std::cout << "last occurrence is at: "
<< std::distance(v.begin(), result) << "\n";
}
}
So in your case:
auto result = std::find_end(array.begin(), array.end(), subSeq.begin(), subSeq.end());
if (result == array.end()) {
std::cout << "sequence not found\n";
} else {
std::cout << "last occurrence is at: "
<< std::distance(array.begin(), result) << "\n";
}

Can a vector::iterator also function as a vector

So I am trying to load a binary file into a Vector, so I can use it like a Buffer.
ifstream binaryFile;
vector<unsigned char> fileBuffer(istreambuf_iterator<char>(binaryFile), {});
vector<unsigned char>::iterator fileIter = fileBuffer.begin();
Now my question is, if I use the fileIter variable, can I access all the elements in the fileBuffer vector ?
I want to know, because I need to edit the contents of the fileBuffer only at certain Positions, that is why I am working with iterators in the first place.
In a nutshell, I want to know if the content of the vector fileBuffer will change, according to edits made to the fileIter with code like
*(fileIter + 2) = 'a';
I have researched this Topic but I have not yet found an answer.
The standard class template std::vector has a random access iterator. So you can use it the same way as a pointer. For example
fileIter[10] = 'A';
or
fileIter += 10;
and so on.
Here is a demonstrative program.
#include <iostream>
#include <vector>
int main()
{
std::vector<int> v = { 1, 2, 3, 4, 5 };
for ( const auto &item : v ) std::cout << item << ' ';
std::cout << '\n';
auto it = v.begin();
it[1] = -it[1];
for ( const auto &item : v ) std::cout << item << ' ';
std::cout << '\n';
it += 2;
*it *= 10;
for ( const auto &item : v ) std::cout << item << ' ';
std::cout << '\n';
return 0;
}
Its output is
1 2 3 4 5
1 -2 3 4 5
1 -2 30 4 5

Most 'functional' way to sum pairs of elements from a vector using C++17 or later?

I'd like some suggestions for the most terse and 'functional' way to gather pairs of successive elements from a vector (1st and 2nd, 3rd and 4th, etc.) using modern C++. Assume the vector is of arbitrary but even length. For the examples I'm pulling together, I'm summing the elements of each pair but that's not the main problem. I should add I'll use STL only, no Boost.
In Python I can zip them into 2-tuples via an iterator with
s = range(1,11)
print([(x + y) for x,y in zip(*[iter(s)] * 2)])
In Perl 5 I can peel off pairs with
use List::Util qw/pairs sum/;
use feature 'say';
#s = 1 .. 10;
say sum #$_ foreach (pairs #s);
In Perl 6 I can shove them two at a time into a block with
my #s = 1 .. 10;
for #s -> $x, $y { say $x + $y; }
and in R I can wrap the vector into a 2-column array and sum the rows with
s <- 1:10
print(apply(matrix(s, ncol=2, byrow=TRUE), 1, sum))
I am not fluent in C++ and my solution uses for(;;). That feels too much like C.
#include <iostream>
#include <vector>
#include <numeric> // std::iota
int main() {
std::vector<int> s(10);
std::iota(s.begin(), s.end(), 1);
for (auto p = s.cbegin(); p != s.cend(); p += 2)
std::cout << (*p + *(p + 1)) << std::endl;
}
The output of course should be some variant of
3
7
11
15
19
Using range-v3:
for (auto v : view::iota(1, 11) | view::chunk(2)) {
std::cout << v[0] + v[1] << '\n';
}
Note that chunk(2) doesn't give you a compile-time-fixed size view, so you can't do:
for (auto [x,y] : view::iota(1, 11) | view::chunk(2)) { ... }
Without using range-v3 I was able to do this with either a function or a lambda template. I'll show the lambda version here.
#include <iostream>
#include <string>
#include <vector>
template<typename T>
auto lambda = [](const std::vector<T>& values, std::vector<T>& results) {
std::vector<T> temp1, temp2;
for ( std::size_t i = 0; i < values.size(); i++ ) {
if ( i & 1 ) temp2.push_back(values[i]); // odd index
else temp1.push_back(values[i]); // even index
}
for ( std::size_t i = 0; i < values.size() / 2; i++ )
results.push_back(temp[i] + temp[2]);
};
int main() {
std::vector<int> values{ 1,2,3,4,5,6 };
for (auto i : values)
std::cout << i << " ";
std::cout << '\n';
std::vector<int> results;
lambda<int>(values, results);
for (auto i : results)
std::cout << i << " ";
std::cout << '\n';
std::vector<float> values2{ 1.1f, 2.2f, 3.3f, 4.4f };
for (auto f : values2)
std::cout << f << " ";
std::cout << '\n';
std::vector<float> results2;
lambda<float>(values2, results2);
for (auto f : results2)
std::cout << f << " ";
std::cout << '\n';
std::vector<char> values3{ 'a', 'd' };
for (auto c : values3)
std::cout << c << " ";
std::cout << '\n';
std::vector<char> results3;
lambda<char>(values3, results3);
for (auto c : results3)
std::cout << c << " ";
std::cout << '\n';
std::vector<std::string> values4{ "Hello", " ", "World", "!" };
for (auto s : values4)
std::cout << s;
std::cout << '\n';
std::vector<std::string> results4;
lambda<std::string>(values4, results4);
for (auto s : results4)
std::cout << s;
std::cout << '\n';
return EXIT_SUCCESS;
}
Output
1 2 3 4 5 6
3 7 11
1.1 2.2 3.3 4.4
3.3 7.7
a d
┼
Hello World!
Hello World!
At the risk of sounding like I'm trying to be clever or annoying, I say this is the answer:
print(sums(successive_pairs(range(1,11))));
Now, of course, those aren't built-in functions, so you would have to define them, but I don't think that is a bad thing. The code clearly expresses what you want in a functional style. Also, the responsibility of each of those functions is well separated, easily testable, and reusable. It isn't necessary to use a lot of tricky specialized syntax to write code in a functional style.