0

I know with transform I can add a constant to some vector like so:

std::vector<int> a(3, 2);
std::transform( a.begin(), a.end(), a.begin(), std::bind2nd( std::plus<double>(), 1 ) );

I was wondering how I could modify transform to add a constant to some slice [index:end] of the vector, e.g. the last two elements.

I can do it with a loop e.g.:

for (int i=1; i < a.size(); i++) {
    a.at(i) += 1;
}

but maybe there's a better option

eok
  • 151
  • 8
  • I tried that also, but oddly enough that yields `3 3 2` instead of `2 3 3` – eok May 01 '19 at 09:05
  • Sorry, you'd need to add to both the begin iterators. – George May 01 '19 at 09:10
  • How do you think your for loop is not optimal, ie. in what way do you want to make it better? Easier to understand for you later? Easier to understand for future developer reading your code? Just less code? More efficient after compiler optimizations? – hyde May 01 '19 at 10:29
  • I don't know, I'm not very familiar with C++ and I read one should use the `` library... not sure if that then includes using `transform` as well @hyde – eok May 01 '19 at 10:31
  • @eok It doesn't, the point of the std algorithms is that they're generic. They can use iterators of many different list types including but not limited to `std::vector`, `std::list`, `std::array` etc... – George May 01 '19 at 12:11
  • `bind2nd` is deprecated in C++11 and removed in C++17. – L. F. May 01 '19 at 13:44

4 Answers4

2

Just the last N elements? Use a reverse iterator:

#include <algorithm>
#include <iostream>
#include <iterator>
#include <vector>

int main() {
  std::vector<int> a(3, 2);
  std::transform(a.rbegin(), std::next(a.rbegin(), 2), a.rbegin(),
                 [](auto n) { return n + 1; });

  for (auto n : a) {
    std::cout << n << '\n';
  }
  return 0;
}

(Using std::next() instead of just a.rbegin() + 2 makes it easier if you for some reason need to change a to a container type that doesn't have random iterators.)

Shawn
  • 47,241
  • 3
  • 26
  • 60
2

The reason algorithms take iterators, not containers, is exactly for cases like this.

The general method to adapt a Python style slice is to add +ve values to begin and -ve to end. Assuming there are at least two elements, incrementing last two elements would be

auto start = a.end() - 2;
auto finish = a.end();
std::transform( start, finish , start, std::bind2nd( std::plus<double>(), 1 ) );
Caleth
  • 52,200
  • 2
  • 44
  • 75
1

The same code after a small refactor:

std::vector< int > a(3, 2);

auto start = a.begin();
auto end   = a.end();
auto func  = [](auto val) {
    return val + 1;
};

std::transform(start, end, start, func);

Now by modifying the values of start and end, only the slice can be modified.

For this std::advance can be used:

std::advance(start, 1);
std::advance(end, -1);

In the above code:

  • start is 1 element after the beginning (2nd element - element with index of 1).
  • end is 1 element from the end.
Robert Andrzejuk
  • 5,076
  • 2
  • 22
  • 31
1

You could use a span (std::span in C++20, gsl::span earlier) to represent your slice. If you don't know about spans, have a look at:

What is a "span" and when should I use one?

So if you write,

// ... vector a gets defined
auto slice = std::span(a).subspan(some_index);

The slice now behaves just like any standard library container. Specifically, you could write:

std::transform(slice.begin(), slice.end(), slice.begin(), [](auto v) { return v + 1; });
einpoklum
  • 118,144
  • 57
  • 340
  • 684