C++. STL. Алгоритмы. Алгоритмы, изменяющие все элементы последовательности

Модифицирующие алгоритмы. Часть 1.
Алгоритмы, изменяющие все элементы последовательности


Содержание


Поиск на других ресурсах:

1. Алгоритм for_each. Выполнить операцию над каждым элементом последовательности.

Алгоритм for_each позволяет делать заданную операцию над каждым элементом последовательности. Эта операция задается функцией. Общая форма распространенной реализации алгоритма выглядит следующим образом

template <class InputIterator, class Function>
Function for_each(
  InputIterator first,
  InputIterator last,
  Function func);

здесь

  • first – итератор ввода, указывающий на первый элемент диапазона, над которым нужно выполнить операцию;
  • last – итератор ввода, указывающий на последний элемент диапазона, над которым нужно выполнить операцию;
  • func – функция, определяющая операцию.

Пример 1.

В примере каждый элемент вектора умножается на 2. Для умножения используется функция Mult2().

#include <iostream>
#include <vector>
#include <queue>
#include <list>
#include <algorithm>
#include <functional>
using namespace std;

void Mult2(int& value)
{
  value = value * 2;
}

void main()
{
  // Алгоритм for_each. Выполнить операцию над каждым элементом последовательности
  // 1. Создать динамический массив
  vector<int> V = { 4, 8, 9, 2, 10, 15 };

  // 2. Вызвать алгоритм for_each
  // Здесь используется функция Mult2()
  for_each(V.begin(), V.end(), Mult2); // каждый элемент вектора умножается на 2

  // 3. Вывести результирующий массив
  vector<int>::iterator p1;

  p1 = V.begin();
  while (p1 != V.end())
  {
    cout << *p1 << " ";
    p1++;
  }

  cout << endl;
}

Пример 2.

В нашем примере каждая строка списка list<string> реверсируется с помощью алгоритма for_each. Функция реверсирования Reverse().

#include <iostream>
#include <list>
#include <algorithm>
#include <functional>
using namespace std;

// Функция реверсирующая строку
void Reverse(string& s)
{
  string s2 = "";
  for (int i = 0; i < s.length(); i++)
  {
    s2 = s[i] + s2;
  }
  s.assign(s2);
}

void main()
{
  // Алгоритм for_each. Выполнить операцию над каждым элементом последовательности
  // 1. Создать список строк
  list<string> L = {
    "abc",
    "def",
    "ghi",
    "jkl"
  };

  // 2. Вызвать алгоритм for_each
  // Здесь используется функция Mult2()
  for_each(L.begin(), L.end(), Reverse); // кажый элемент списка реверсируется

  // 3. Вывести результирующий список
  list<string>::iterator it;

  it = L.begin();
  while (it != L.end())
  {
    cout << *it << endl;
    it++;
  }

  cout << endl;
}

Результат

cba
fed
ihg
lkj

 

2. Алгоритм transform. Выполнить действие для каждого элемента последовательности

Алгоритм transform позволяет задать операцию, выполняемую над каждым элементом последовательности или над парой элементов в двух выходных диапазонах. Операция может быть задана унарной или бинарной функцией. Объявление наиболее распространенных реализаций алгоритма имеет вид

template <class InputIterator, class OutputIterator, class UnaryFunction>
OutputIterator transform(
  InputIterator first1,
  InputIterator last1,
  OutputIterator result,
  UnaryFunction func );

template <class InputIterator1, class InputIterator2, class OutputIterator, class BinaryFunction>
OutputIterator transform(
  InputIterator1 first1,
  InputIterator1 last1,
  InputIterator2 first2,
  OutputIterator result,
  BinaryFunction func );

здесь

  • first1, last1 – итераторы ввода, задающие первый выходной диапазон для обработки;
  • first2 – итератор ввода, задающий второй диапазон для обработки;
  • result – итератор вывода, указывающий на позицию первого элемента в диапазоне назначения;
  • func – унарная или бинарная функция, задающая операцию, которую нужно выполнить над одним элементом или над несколькими элементами.

Пример.

#include <iostream>
#include <list>
#include <algorithm>
using namespace std;

// Предикат, выполняющий операцию над отдельным элементом последовательности,
// умножить значение параметра value на 5
int Mult_5(int value)
{
  return value * 5;
}

//Предикат, выполняющий операцию над двумя отдельными элементами.
// Эти элементы могут быть из разных последовательностей.
int Mult_AB(int a, int b)
{
  return a * b; // вернуть произведение элементов
}

int main()
{
  // Алгоритм transform – выполнить функцию для каждого элемента этой последовательности

  // 1. Использование алгоритма в сочетании с предикатом
  // 1.1. Создать последовательность чисел и вывести ее на консоль
  list<int> L1 = { 2, 8, 1, 4, 3, 5, 7, 0, -2, 6, 3 };
  cout << "L1 => ";
  for (int i : L1)
    cout << i << " ";
  cout << endl;

  // 1.2. Создать другую последовательность размером исходной
  list<int> L2(L1.size());

  // 1.3. Вызвать алгоритм transform для списка L1 на основе предиката Mult_5,
  // создается новая последовательность L2
  transform(L1.begin(), L1.end(), L2.begin(), Mult_5);

  // 1.4. Вывести результирующую последовательность L2
  cout << "L2 => ";
  for (int i : L2)
    cout << i << " ";
  cout << endl;

  // 2. Использование алгоритма на основе лямбда-выражения
  // 2.1. Создать последовательность L3 нулевых значений размером L1.size()
  list<int> L3(L1.size());

  // 2.2. Создать вторую последовательность, каждый элемент которой
  // является умноженным на 2 – вызвать алгоритм transform
  transform(L1.begin(), L1.end(), L3.begin(), [](int a) { return a * 2; });

  // 2.3. Вывести результирующую последовательность
  cout << "L3 => ";
  for (int i : L3)
    cout << i << " ";
  cout << endl;

  // 3. Использование алгоритма transform для выполнения операции над двумя последовательностями.
  // Необходимо умножить элементы последовательности L1 на элементы последовательности L2

  // 3.1. Создать новую последовательность размером L1.size()
  list<int> L4(L1.size());

  // 3.2. Заполнить последовательность L4 значениями L1[i]*L2[i]
  transform(L1.begin(), L1.end(), L2.begin(), L4.begin(), Mult_AB);

  // 3.3. Вывести последовательность L4
  cout << "L1[i]*L2[i] => ";
  for (int i : L4)
    cout << i << " ";
  cout << endl;

  // 4. Использование алгоритма transform для выполнения операции над двумя последовательностями
  // на основе лямбда-выражения.
  // Получить новую последовательность, в которой добавляются поэлементно последовательности L1 и L2.
  // 4.1. Создать новую нулевую последовательность L5 размером L1.size().
  list<int> L5(L1.size());

  // 4.2. Заполнить последовательность L5 значениями L1[i]+L2[i] на основе лямбда-выражения.
  transform(
    L1.begin(), // начало L1
    L1.end(),   // конец L1, L1.end()-L1.begin() => это длина результирующей последовательности
    L2.begin(), // начало второй последовательности, прилагаемой к L1
    L5.begin(), // начало результирующей последовательности
    [](int a, int b) { return a + b; } // лямбда-выражение, возвращающее сумму параметров a и b
  );

  // 4.3. Вывести результирующую последовательность
  cout << "L1[i]+L2[i] => ";
  for (int i : L5)
    cout << i << " ";
  cout << endl;
}

Результат

L1 => 2 8 1 4 3 5 7 0 -2 6 3
L2 => 10 40 5 20 15 25 35 0 -10 30 15
L3 => 4 16 2 8 6 10 14 0 -4 12 6
L1[i]*L2[i] => 20 320 5 80 45 125 245 0 20 180 45
L1[i]+L2[i] => 12 48 6 24 18 30 42 0 -12 36 18

 

3. Алгоритм fill. Заполнить диапазон нужными значениями

Алгоритм fill предназначен для присвоения каждому элементу в заданном диапазоне некоторого значения. Объявление алгоритма имеет вид

template <class ForwardIterator, class Type>
void fill(
  ForwardIterator first,
  ForwardIterator last,
  const Type& value);

здесь

  • first, last – прямые итераторы, используемые для задания диапазона;
  • value – значение, которым заполняется диапазон.

Пример.

#include <iostream>
#include <queue>
#include <vector>
#include <list>
#include <functional>
#include <algorithm>
using namespace std;

int main()
{
  // Алгоритм fill - заполнить диапазон нужными значениями

  // 1. Объявление вектора из 5 элементов
  vector<int> V(5);

  // 2. Заполнить вектор значением 10
  fill(V.begin(), V.end(), 10);

  // 3. Вывести вектор на экран
  cout << "V => ";
  for (int i : V)
    cout << i << " ";
  cout << endl;

  // 4. Объявить список из 10 элементов
  list<double> L(10);

  // 5. Заполнить список значением 5.0
  fill(L.begin(), L.end(), 5.0);

  // 6. Вывести список на экран
  cout << "L => ";
  for (double d : L)
    cout << d << " ";
  cout << endl;
}

Результат

V => 10 10 10 10 10
L => 5 5 5 5 5 5 5 5 5 5

 

4. Алгоритм fill_n. Заполнить диапазон нужными значениями

Алгоритм fill_n присваивает новое значение указанному количеству элементов в диапазоне начиная с определенного элемента.

Распространенная реализация алгоритма следующая

template <class OutputIterator, class Size, class Type>
OutputIterator fill_n(
  OutputIterator first,
  Size count,
  const Type& value);

здесь

  • first – итератор, указывающий на начало диапазона;
  • count – количество присваиваемых элементов;
  • value – значение, которое присваивается элементам диапазона.

Пример.

#include <iostream>
#include <queue>
#include <vector>
#include <list>
#include <functional>
#include <algorithm>
using namespace std;

int main()
{
  // Алгоритм fill - заполнить диапазон нужными значениями

  // 1. Объявить вектор из 5 элементов
  vector<int> V(5);

  // 2. Заполнить первые 3 элемента вектора значением 10
  fill_n(V.begin(), 3, 10);

  // 3. Вывести вектор на экран
  cout << "V => ";
  for (int i : V)
    cout << i << " ";
  cout << endl;

  // 4. Объявить список из 10 элементов
  list<double> L(10);

  // 5. Заполнить первые 7 элементов списка значением 1.0
fill_n(L.begin(), 7, 1.0);

  // 6. Вывести список на экран
  cout << "L => ";
  for (double d : L)
    cout << d << " ";
  cout << endl;
}

Результат

V => 10 10 10 0 0
L => 1 1 1 1 1 1 1 0 0 0

 

5. Алгоритм generate. Выполнить операцию над каждым элементом последовательности

Алгоритм generate присваивает значения каждому элементу в диапазоне создаваемым объектом функции. Общая форма алгоритма следующая

template <class ForwardIterator, class Generator>
void generate(
  ForwardIterator first,
  ForwardIterator last,
  Generator gen);

здесь

  • first, last – итераторы, определяющие диапазон обрабатываемых значений;
  • gen – объект функции, вызываемой без аргументов для создания значений, присваиваемых каждому элементу в диапазоне.

Пример 1.

В примере с помощью лямбда-выражения записывается значение «abcd» в каждой строке списка L.

#include <list>
#include <algorithm>
#include <functional>
using namespace std;

void main()
{
  // Алгоритм generate. Выполнить операцию над каждым элементом последовательности
  // 1. Создать список строк
  list<string> L = {
    "abc",
    "def",
    "ghi",
    "jkl"
  };

  // 2. Вызвать алгоритм generate
  // Записать "abcd" в каждый элемент списка с помощью лямбда-выражения
  generate(L.begin(), L.end(), []() { return "abcd"; });

  // 3. Вывести результирующий список
  list<string>::iterator it;

  it = L.begin();
  while (it != L.end())
  {
    cout << *it << endl;
    it++;
  }

  cout << endl;
}

Пример 2.

В примере генерируется массив чисел 5.0 путём вызова функции Get5().

#include <iostream>
#include <vector>
#include <algorithm>
#include <functional>
using namespace std;

// Функция, возвращающая число 5
double Get5()
{
  return 5.0;
}

void main()
{
  // Алгоритм generate. Выполнить операцию над каждым элементом последовательности
  // 1. Создать массив из 10 чисел
  vector<double> V(10);

  // 2. Вызвать алгоритм generate
  // Записать число в каждый элемент массива с помощью функции Get5()
  generate(V.begin(), V.end(), Get5);

  // 4. Вывести результирующий массив
  vector<double>::iterator it;

  it = V.begin();
  while (it != V.end())
  {
    cout << *it << " ";
    it++;
  }

  cout << endl;
}

Результат

5 5 5 5 5 5 5 5 5 5

 

6. Алгоритм generate_n. Выполнить операцию над каждым элементом последовательности

Алгоритм generate_n присваивает значение заданному количеству элементов последовательности, которые были созданы объектом функции. Алгоритм возвращает позицию за последним присвоенным значением

template <class OutputIterator, class Diff, class Generator>
void generate_n(
  OutputIterator first,
  Diff count,
  Generator gen);

здесь

  • first – итератор, указывающий на начало диапазона;
  • count – количество элементов, которым присваивается значение;
  • gen – объект функции без аргументов, используемый для формирования значений.

Пример 1.

#include <iostream>
#include <list>
#include <algorithm>
#include <functional>
using namespace std;

void main()
{
  // Алгоритм generate_n. Выполнить операцию над каждым элементом последовательности
  // 1. Создать список строк
  list<string> L;

  // 2. Создать 5 пустых строк
  L.resize(5, "");

  // 3. Вызвать алгоритм generate_n
  // Записать "abcd" в каждый элемент списка с помощью лямбда-выражения
  generate_n(L.begin(), L.size(), []() { return "abcd"; });

  // 4. Вывести результирующий список
  list<string>::iterator it;

  it = L.begin();
  while (it != L.end())
  {
    cout << *it << endl;
    it++;
  }

  cout << endl;
}

Пример 2.

В примере формируется массив случайных чисел, значение которых лежит в диапазоне [1; 10].

#include <iostream>
#include <vector>
#include <algorithm>
#include <functional>
#include <time.h>
using namespace std;

// Функция, возвращающая случайное число в пределах от 1 до 10 включительно
int Get_1_10()
{
  int value = rand() % 10 + 1;
  return value;
}

void main()
{
  // Алгоритм generate_n. Выполнить операцию над каждым элементом последовательности
  // 1. Создать массив из 10 чисел
  vector<int> V(10);

  // 2. Инициализировать ядро для получения случайных значений
  srand(time(NULL));

  // 2. Вызвать алгоритм generate_n
  // Записать число в каждый элемент массива с помощью функции Get_1_10()
  generate_n(V.begin(), V.size(), Get_1_10);

  // 4. Вывести результирующий массив
  vector<int>::iterator it;

  it = V.begin();
  while (it != V.end())
  {
    cout << *it << " ";
    it++;
  }

  cout << endl;
}

 


Связанные темы