Чтение онлайн

на главную - закладки

Жанры

Эффективное использование STL
Шрифт:

Фасет collate

Если вы знакомы с локальными контекстами C++, возможно, вам уже пришел в голову другой способ сравнения строк. У фасета

collate
, предназначенного для инкапсуляции технических аспектов сортировки, имеется функция, по интерфейсу весьма близкая к библиотечной функции C
strcmp
. Существует даже специальное средство, упрощающее сравнение двух строк: для объекта локального контекста
L
строки 
x
и 
y
могут сравниваться простой записью
L(x, y)
, что позволяет обойтись без хлопот, связанных с вызовом
use_facet
и функции
collate
.

«Классический»

локальный контекст содержит фасет
collate
, который выполняет лексикографическое сравнение по аналогии с функцией
operator<
контейнера
string
, но другие локальные контексты выполняют сравнение, руководствуясь своими критериями. Если в системе существует локальный контекст, обеспечивающий сравнение строк без учета регистра для интересующих вас языков, воспользуйтесь им. Возможно, этот локальный контекст даже не будет ограничиваться простым сравнением отдельных символов!

К сожалению, какой бы справедливой ни была эта рекомендация, она никак не поможет тем, у кого нет таких локальных контекстов. Возможно, когда-нибудь в будущем стандартное множество таких локальных контекстов будет стандартизировано, но сейчас никаких стандартов не существует. Если функция сравнения без учета регистра для вашей системы еще не написана, вам придется сделать это самостоятельно.

Сравнение строк без учета регистра

Фасет

ctype
позволяет относительно легко организовать сравнение строк без учета регистра на основе сравнения отдельных символов. Приведенная ниже версия не оптимальна, но по крайней мере она верна. В ней используется практически тот же принцип, что и прежде: строки сравниваются алгоритмом
lexicographical_compare
, а отдельные символы сравниваются после приведения к верхнему регистру. Впрочем, на этот раз вместо глобальной переменной используется объект локального контекста. Кстати говоря, сравнение после приведения обоих символов к верхнему регистру не всегда дает тот же результат, что и после приведения к нижнему регистру. Например, во французском языке в символах верхнего регистра принято опускать диакритические знаки, вследствие чего вызов
toupper
во французском локальном контексте может приводить к потере информации: символы 'ё' и 'е' преобразуются в один символ верхнего регистра 'Е'. В этом случае при сравнении на базе функции
toupper
символы ''e' и 'е' будут считаться одинаковыми, а при сравнении на базе
tolower
они будут считаться разными. Какой из ответов правилен? Вероятно, второй, но на самом деле все зависит от языка, национальных обычаев и специфики приложения.

struct lt_str_1:

 public std::binary_function<std::string, std::string, bool> {

 struct lt_char {

const std::ctype<char>& ct;

 lt_char(const std::ctype<char>& c):ct(c) {}

 bool operator(char x, char y) const {

return ct.toupper(x) < ct.toupper(y);

 }

};

std::locale loc;

const std::ctype<char>& ct;

lt_str_l(const std::locale& L = std::locale::classic):

 loc(L), ct(std::use_facet<std::ctype<char> >(loc)) {}

bool operator(const std::string& x, const std::string& y) const {

 return std::lexicographical_compare(x.begin, x.end,

y.begin, y.end, lt_char(ct));

 }

};

Данное

решение не оптимально; оно работает медленнее, чем могло бы работать. Проблема чисто техническая: функция
toupper
вызывается в цикле, а Стандарт C++ требует, чтобы эта функция была виртуальной. Некоторые оптимизаторы выводят вызов виртуальной функции из цикла, но чаще этого не происходит. Циклические вызовы виртуальных функций нежелательны.

В данном случае тривиального решения не существует. Возникает соблазнительная мысль — воспользоваться одной из функций объекта

ctype
:

const char* ctype<char>::toupper(char* f, char* i) const

Эта функция изменяет регистр символов в интервале

[f, i]
. К сожалению, для наших целей этот интерфейс не подходит. Чтобы воспользоваться этой функцией для сравнения двух строк, необходимо скопировать обе строки в буферы и затем преобразовать их содержимое к верхнему регистру. Но откуда возьмутся эти буферы? Они не могут быть массивами фиксированного размера (неизвестно, каким должен быть максимальный размер), а динамические массивы потребуют дорогостоящего выделения памяти.

Альтернативное решение заключается в однократном преобразовании каждого символа с кэшированием результата. Такое решение недостаточно универсально — в частности, при использовании 32-разрядных символов UCS-4 оно абсолютно неработоспособно. С другой стороны, при работе с типом

char
(8-разрядным в большинстве систем) идея хранения 256 байт дополнительных данных в объекте функции сравнения выглядит вполне реально.

struct lt_str_2:

 public std::binary_function<std::string, std::string, bool> {

 struct lt_char {

const char* tab;

lt_char(const char* t) : tab(t) {}

bool operator (char x, char y) const {

return tab[x-CHAR_MIN] < tab[y-CHAR_MIN];

 }

};

char tab[CHAR_MAX-CHAR_MIN+1];

lt_str_2(const std::locale& L = std::locale::classic) {

 const std::ctype<char>& ct = std::use_facet<std::ctype<char> >(L);

 for (int i = CHAR_MIN; i<=CHAR_MAX; ++i) tab[i-CHAR_MIN]=(char)i;

 ct.toupper(tab, tab+(CHAR_MAX-CHAR_MIN+1));

}

bool operator(const std::string& x, const std::string& y) const {

 return std::lexicographical_compare(x.begin, x.end,

y.begin, y.end, lt_char(tab));

 }

}

Как видите, различия между

lt_str_1
и
lt_str_2
не так уж велики. В первом случае используется объект функции сравнения символов, использующий фасет
ctype
напрямую, а во втором случае — объект функции сравнения с таблицей заранее вычисленных преобразований символов к верхнему регистру. Второе решение уступает первому, если создать объект функции
lt_str_2
, воспользоваться им для сравнения нескольких коротких строк и затем уничтожить. С другой стороны, при обработке больших объемов данных
lt_str_2
работает значительно быстрее
lt_str_1
. В моих тестах превосходство было более чем двукратным: при использовании
lt_str_1
сортировка списка из 23 791 слова заняла 0,86 секунды, а при использовании
lt_str_2
понадобилось только 0,4 секунды.

Поделиться:
Популярные книги

Зубных дел мастер

Дроздов Анатолий Федорович
1. Зубных дел мастер
Фантастика:
научная фантастика
попаданцы
альтернативная история
5.00
рейтинг книги
Зубных дел мастер

Бывшие. Война в академии магии

Берг Александра
2. Измены
Любовные романы:
любовно-фантастические романы
7.00
рейтинг книги
Бывшие. Война в академии магии

Идеальный мир для Лекаря 7

Сапфир Олег
7. Лекарь
Фантастика:
юмористическая фантастика
попаданцы
аниме
5.00
рейтинг книги
Идеальный мир для Лекаря 7

Возвышение Меркурия. Книга 3

Кронос Александр
3. Меркурий
Фантастика:
попаданцы
аниме
5.00
рейтинг книги
Возвышение Меркурия. Книга 3

Кодекс Охотника. Книга X

Винокуров Юрий
10. Кодекс Охотника
Фантастика:
фэнтези
попаданцы
аниме
6.25
рейтинг книги
Кодекс Охотника. Книга X

Хозяин Теней

Петров Максим Николаевич
1. Безбожник
Фантастика:
попаданцы
аниме
фэнтези
5.00
рейтинг книги
Хозяин Теней

Опасная любовь командора

Муратова Ульяна
1. Проклятые луной
Фантастика:
фэнтези
5.00
рейтинг книги
Опасная любовь командора

Друд, или Человек в черном

Симмонс Дэн
Фантастика:
социально-философская фантастика
6.80
рейтинг книги
Друд, или Человек в черном

Волхв

Земляной Андрей Борисович
3. Волшебник
Фантастика:
попаданцы
альтернативная история
5.00
рейтинг книги
Волхв

Мастер Разума VII

Кронос Александр
7. Мастер Разума
Фантастика:
боевая фантастика
попаданцы
аниме
5.00
рейтинг книги
Мастер Разума VII

Всемирная энциклопедия афоризмов. Собрание мудрости всех народов и времен

Агеева Елена А.
Документальная литература:
публицистика
5.40
рейтинг книги
Всемирная энциклопедия афоризмов. Собрание мудрости всех народов и времен

Сумеречный Стрелок 5

Карелин Сергей Витальевич
5. Сумеречный стрелок
Фантастика:
городское фэнтези
попаданцы
аниме
5.00
рейтинг книги
Сумеречный Стрелок 5

Морской волк. 1-я Трилогия

Савин Владислав
1. Морской волк
Фантастика:
альтернативная история
8.71
рейтинг книги
Морской волк. 1-я Трилогия

Прогрессор поневоле

Распопов Дмитрий Викторович
2. Фараон
Фантастика:
попаданцы
альтернативная история
5.00
рейтинг книги
Прогрессор поневоле