Отсеиваем повторяющиеся слова из пользовательского ввода и выводим их на экран в алфавитном порядке с помощью контейнера std::set
Контейнер std::set
довольно странный. Принцип его работы похож на принцип работы контейнера std::map
. Однако std::set
содержит только ключи в качестве значений, а не пары «ключ — значение». Поэтому он не очень подходит для соотношения значения одного типа со значениями другого. Поскольку способы применения этого контейнера не вполне очевидны, многие разработчики даже не знают о его существовании. Они часто начинают реализовывать похожие механизмы самостоятельно, хотя в некоторых ситуациях контейнер std::set
оказался бы отличным подспорьем.
В этом разделе будет показано, как применить контейнер std::set
, на примере, в котором мы получаем потенциально большое количество разных элементов. Мы
Как это делается
В этом примере мы считаем поток слов из стандартного средства ввода. Все std::set
. Таким образом мы сможем перечислить все уникальные слова из потока[1].
1. Мы используем несколько разных типов STL, для чего включим некоторые заголовочные файлы:
#include
#include
#include
#include
2. Чтобы сэкономить немного времени на наборе текста, объявим об использовании пространства имен std
:
using namespace std;
3. Теперь мы готовы писать саму программу, начинающуюся с функции main
. В ней создается экземпляр класса std::set
, в котором будут храниться строки:
int main()
{
set
4. Далее получим данные от пользователя. Просто считаем их из стандартного потока ввода с помощью удобного итератора istream_iterator
:
istream_iterator
istream_iterator
5. Имея начальный и конечный итераторы, которые представляют данные, введенные пользователем, можем просто заполнить множество на основе этих данных с помощью std::inserter
:
copy(it, end, inserter(s, s.end()));
6. На этом, в общем-то, все. Чтобы увидеть, какие
for (const auto word : s) {
cout << word << ", ";
}
cout << '\n';
}
7. Скомпилируем и запустим программу с нашими входными данными. Взглянем на полученный результат, из которого были удалены все дубликаты, а уникальные слова теперь отсортированы по алфавиту:
$ echo "a a a b c foo bar foobar foo bar bar" | ./program
a, b, bar, c, foo, foobar,
Как это работает
В данной программе можно отметить два интересных момента. Первый из них состоит в том, что мы применяем итератор std::istream_iterator
, чтобы получить доступ к данным, введенным пользователем. Второй момент: для записи этих данных в наш контейнер std::set
мы задействуем алгоритм std::copy
, который обернули в экземпляр класса std::inserter
! Может показаться удивительным то, что всего одна строка кода выполняет всю работу по
std::istream_iterator
Этот класс очень интересен в тех случаях, когда мы хотим обрабатывать большие объемы std::string
.
Итератор std::istream_iterator
принимает один шаблонный параметр с необходимым нам типом. Мы выбрали тип std::string
, поскольку ожидаем, что будем работать со словами, но это могут быть, например, и числа с плавающей точкой. По сути, здесь годится любой тип, для которого можно записать cin>>var;
. Конструктор принимает экземпляр класса istream
. Стандартный поток ввода представляется глобальным объектом потока ввода std::cin
, который вполне подходит для нашего случая.
istream_iterator
К созданному итератору потока ввода применимы две операции. Во-первых, при разыменовании (*it
) он возвращает текущий введенный символ. Поскольку мы указали, что итератор соотнесен с типом std::string
с помощью шаблонного параметра, этот символ будет содержать одно слово. Во-вторых, при инкременте (++it
) итератор переходит на следующее слово, к которому мы также можем получить доступ путем разыменования.