Шрифт:
Интервал:
Закладка:
21.2.1. Примеры использования обобщенных алгоритмов

• Алгоритм find() можно применять к любой последовательности в стиле библиотеки STL.
• Алгоритм find() можно применять к любому типу элементов.
Рассмотрим несколько примеров (если они покажутся вам сложными, посмотрите на диаграммы из раздела 20.4).
void f(vector<int>& v,int x) // работает с целочисленными векторами
{
vector<int>::iterator p = find(v.begin(),v.end(),x);
if (p!=v.end()) { /* мы нашли x */ }
// ...
}

Попробуем применить алгоритм к объекту класса list.
void f(list<string>& v,string x) // работает со списком строк
{
list<string>::iterator p = find(v.begin(),v.end(),x);
if (p!=v.end()) { /* мы нашли x */ }
// ...
}

Итак, алгоритм find() чрезвычайно гибкий: если мы будем соблюдать простые правила работы с итераторами, то сможем использовать алгоритм find() для поиска элементов в любой последовательности любого контейнера. Например, с помощью алгоритма find() мы можем искать символ в объекте класса Document, определенного в разделе 20.6.
void f(Document& v,char x) // работает с объектами класса Document
{
Text_iterator p = find(v.begin(),v.end(),x);
if (p!=v.end()) { /* мы нашли x */ }
// ...
}
Эта гибкость является отличительной чертой алгоритмов из библиотеки STL и делает их более полезными, чем многие люди могут себе представить.
21.3. Универсальный алгоритм поиска: find_if()
Нам редко приходится искать какое-то конкретное значение. Чаще нас интересует значение, удовлетворяющее определенным критериям. Мы смогли бы выполнять намного более полезную операцию find, если бы могли определять свои собственные критерии поиска. Например, мы могли бы найти число, превышающее 42. Мы могли бы также сравнивать строки, не учитывая регистр (верхний или нижний).
Кроме того, мы могли найти первое нечетное число. А может, мы захотели бы найти запись с адресом "17 Cherry Tree Lane".
Стандартный алгоритм поиска в соответствии с критерием, заданным пользователем, называется find_if().
template<class In,class Pred>
In find_if(In first,In last,Pred pred)
{
while (first!=last && !pred(*first)) ++first;
return first;
}
Очевидно (если сравнить исходные коды), что он похож на алгоритм find(), за исключением того, что в нем используется условие !pred(*first), а не *first!=val; иначе говоря, алгоритм останавливает поиск, как только предикат pred() окажется истинным, а не когда будет обнаружен элемент с заданным значением.

bool odd(int x) { return x%2; } // % — деление по модулю
void f(vector<int>& v)
{
vector<int>::iterator p = find_if(v.begin(), v.end(), odd);
if (p!=v.end()) { /* мы нашли нечетное число */ }
// ...
}
При данном вызове алгоритм find_if() применит функцию odd() к каждому элементу, пока не найдет первое нечетное число. Аналогично, мы можем найти первый элемент списка, значение которого превышает 42.
bool larger_than_42(double x) { return x>42; }
void f(list<double>& v)
{
list<double>::iterator p = find_if(v.begin(), v.end(),
larger_than_42);
if (p!=v.end()) { /* мы нашли значение, превышающее 42 */ }
// ...
}
Однако последний пример не вполне удовлетворительный. А что, если мы после этого захотим найти элемент, который больше 41? Нам придется написать новую функцию. Хотите найти элемент, который больше 19? Пишите еще одну функцию. Должен быть более удобный способ!
Если мы хотим сравнивать элемент с произвольным значением v, то должны как-то сделать это значение неявным аргументом предиката алгоритма find_if(). Мы могли бы попробовать (выбрав в качестве удобного имени идентификатор v_val).
double v_val; // значение, с которым предикат larger_than_v()
// сравнивает свой аргумент
bool larger_than_v(double x) { return x>v_val; }
void f(list<double>& v,int x)
{
v_val = 31; // устанавливаем переменную v_val равной 31,
// для следующего вызова предиката larger_than_v
list<double>::iterator p =