Я знаю, с помощью transform я могу добавить константу в некоторый вектор, например, так:

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

Мне было интересно, как я мог бы изменить transform для добавления константы к некоторому срезу [index:end] вектора, например, последние два элемента.

Я могу сделать это с помощью петли, например:

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

Но возможно есть лучший вариант

0
eok 1 Май 2019 в 12:03

4 ответа

Лучший ответ

Только последние N элементы? Используйте обратный итератор:

#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;
}

(Использование std::next() вместо простого a.rbegin() + 2 облегчает задачу, если вам по какой-то причине необходимо заменить a на тип контейнера, в котором нет случайных итераторов.)

2
Shawn 1 Май 2019 в 09:35

Причина, по которой алгоритмы используют итераторы, а не контейнеры, именно для таких случаев.

Общий способ адаптации среза в стиле Python состоит в добавлении +ve значений в begin и -ve в end. Предполагая, что есть как минимум два элемента, увеличение последних двух элементов будет

auto start = a.end() - 2;
auto finish = a.end();
std::transform( start, finish , start, std::bind2nd( std::plus<double>(), 1 ) );
2
Caleth 1 Май 2019 в 11:42

Вы можете использовать диапазон (std::span в C ++ 20, gsl::span ранее) для представления своего среза. Если вы не знаете о пролетах, взгляните на:

Что такое «промежуток» и когда я должен его использовать?

Так что, если вы напишите,

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

Теперь срез ведет себя так же, как любой стандартный контейнер библиотеки. В частности, вы могли бы написать:

std::transform(slice.begin(), slice.end(), slice.begin(), [](auto v) { return v + 1; });
1
einpoklum 1 Май 2019 в 13:30

Тот же код после небольшого рефакторинга:

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);

Теперь, изменив значения start и end, можно изменить только срез.

Для этого можно использовать std::advance:

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

В приведенном выше коде:

  • start - это 1 элемент после начала (2-й элемент - элемент с индексом 1).
  • end - это 1 элемент с конца.
1
Robert Andrzejuk 1 Май 2019 в 11:04