Pull to refresh

Использование алгоритмов стандартой библиотеки вместо циклов

Думаю многие из тех, кто начали читать эту статью уже сталкивались с задачами сортировки массивов и прочих вещей, а так-же знают о том, что есть std::sort, но не все знают о существовании готовых предикатов.

Предикаты

Рассмотрим код на примере обычной сортировки вектора.

vector<int> v{ 14,3,-2,7 };
sort(v.begin(), v.end(), [](int a, int b) { return a > b; }); // 14 7 3 -2

Здесь с помощью лямбды мы сортируем вектор по убыванию, но что бы лишний раз не писать лямбду, можно просто написать предикат std::greater.

vector<int> v{ 14,3,-2,7 };
 sort(v.begin(), v.end(), std::greater<int>{}); // 14 7 3 -2 

Так-же существуют и другие, например std:less или std::equal_to

Устройство подобных предикатов очень простое, создам свой аналог предиката std:less

template<typename T>
struct myless {
  operator bool()(const T& lhs, const T& rhs) {
    return lhs < rhs;
  }
};

Использование подобной штуки очень простое

Для начала нам надо инициализировать объект, а далее уже использовать оператор круглые скобки. Например, предикат myless<int>{}(2,3)будет равен true.

std::find_if, std::copy_if, ….

Задача: Даны два int вектора, нужно скопировать все положительные элементы из одного, в другой. Около года назад я бы сделал такую реализацию.

vector<int> from{ -5,2,6,-14,7 };
vector<int> to;
for (auto& el : from) { 
  if (el > 0)
    to.push_back(el); 
}

Но согласитесь, что код приведённый ниже будет смотреться гораздо элегантнее.

vector<int> from{ -5,2,6,-14,7 };
vector<int> to; 
 std::copy_if(from.begin(), from.end(), std::back_inserter(to), 
              [](int x) {return x > 0; });

Задача: Написать такую функцию с сигнатурой void foo(string&), что будет удалять все чары из строки не являющиеся какой-либо цифрой. Самое простое решение(логически) которое здесь только можно придумать

void foo(string& x) {
 string temp = "";
 for (auto& el : x) {
   if (el >= '0' && el <= '9')
     temp += el;
 }
 x = temp;
}

Думаю, что многие согласятся с тем, что выглядит это не очень то и красиво, поэтому предлагаю вариант немного получше.

void foo(string& x) {
  std::erase_if(x, [](char s) {return !(s >= '0' && s <= '9'); });
}

Задача: Дан vector<int> и надо все отрицательные числа в нём заменить на нули. Реализация которую сделает большинство новичков, скорее всего будет выглядеть как-то так:

 vector<int> x{ 2,-5,7,-3 };
 for (auto& el : x) { 
   if (el < 0)
     el = 0;
 }

Здесь я нахожу более хороший вариант с использованием std::replace_if

vector<int> x{ 2,-5,7,-3 };
std::replace_if(x.begin(), x.end(), [](int x) {return x < 0; }, 0);

Задача: Функция string foo(string) принимает как аргумент путь к файлу, а возвращает путь к папке, в которой он лежит.

cout << foo("C:\\Windows\\System32\\cmd.exe"); // C:\\Windows\\System32

Для совсем новичков не сходу будет понятно, как именно реализовать такую функцию. Но вспомнив о том, что у basic_string есть метод rfind (поиск справа) можно легко справится с подобной задачей.

string foo(string x) {
 return x.substr(0, x.rfind("\\"));
}

Здесь rfind ищет самое первое вхождение подстроки "\\" с конца(справа) исходной строки.

Tags:
Hubs:
You can’t comment this publication because its author is not yet a full member of the community. You will be able to contact the author only after he or she has been invited by someone in the community. Until then, author’s username will be hidden by an alias.