Наследование, полиморфизм, инкапсуляция, исключения 4 страница

параметров):

copy (istream_iterator<int>(cin), istream_iterator<int>(),

back_inserter(v)); // считать элементы в вектор v

unique_copy (istream_iterator<int>(cin), istream_iterator<int>(),

back_inserter(v)); // считать уникальные элементы в вектор v

ЧАСТЬ 2

ЗАДАНИЯ НА ЛАБОРАТОРНУЮ РАБОТУ

Создать контейнер объектов классов, реализованных в лабораторной работе 5 (студентов, общежитий, преподавателей и т.д.) Контейнер реализовать как вектор. Произвести определенные действия с этим контейнером. Не использовать циклы, использовать только подходящие стандартные алгоритмы. Преобразовать классы для решения данной задачи.

ВАРИАНТ 1

Необходимые задачи таковы:

1. обеспечить для контейнера возможность чтения из файла и записи в файл с использованием итераторов потоков.

2. обеспечить для контейнера сортировку по фамилии студента (это должна быть сортировка по умолчанию), а также сортировку по его среднему баллу за последнюю сессию.

3. вынести в отдельный вектор всех студентов, начиная со студента со средним баллом больше, чем 4

4. сохранить все средние баллы студентов из вектора шага 3 в отдельном векторе

5. узнать, сколько в векторе шага 4 значений среднего балла выше заданного

6. найти средний балл для всех студентов с использованием accumulate()

7. проверить, входит ли вектор, полученный на шаге 3, в исходный вектор как подпоследовательность.

ВАРИАНТ 2

Необходимые задачи таковы:

1. обеспечить для контейнера возможность чтения из файла и записи в файл с использованием итераторов потоков.

2. обеспечить для контейнера сортировку по улице и номеру дома общежития (это должна быть сортировка по умолчанию), а также сортировку по его средней заселенности.

3. вынести в отдельный вектор все общежития, начиная с общежития с числом свободных комнат больше, чем 4

4. сохранить все номера домов из вектора шага 3 в отдельном векторе

5. узнать, сколько в векторе шага 4 номеров домов больше заданного

6. найти среднюю заселенность всех общежитий с использованием accumulate()

7. проверить, входит ли вектор, полученный на шаге 3, в исходный вектор как подпоследовательность.

ВАРИАНТ 3

Необходимые задачи таковы:

1. обеспечить для контейнера возможность чтения из файла и записи в файл с использованием итераторов потоков.

2. обеспечить для контейнера сортировку по полному названию факультета (это должна быть сортировка по умолчанию), а также сортировку по суммарному числу студентов.

3. вынести в отдельный вектор все факультеты, начиная с факультета со средним баллом по всем группам выше, чем 4

4. сохранить все средние баллы из вектора шага 3 в отдельном векторе

5. найти в векторе шага 4 число факультетов со средним баллом выше заданного

6. найти средний балл для всех факультетов с использованием accumulate()

7. проверить, входит ли вектор, полученный на шаге 3, в исходный вектор как подпоследовательность.

ВАРИАНТ 4

Необходимые задачи таковы:

1. обеспечить для контейнера возможность чтения из файла и записи в файл с использованием итераторов потоков.

2. обеспечить для контейнера сортировку по фамилии преподавателя (это должна быть сортировка по умолчанию), а также сортировку по его суммарному количеству часов.

3. вынести в отдельный вектор всех преподавателей, начиная с преподавателя, выставившего средний балл больше, чем 4

4. сохранить все количества часов преподавателей из вектора шага 3 в новом векторе.

5. найти число преподавателей с количеством часов больше заданного в векторе, полученном на шаге 4.

6. найти средний балл по всем предметам с использованием accumulate()

7. проверить, входит ли вектор, полученный на шаге 3, в исходный вектор как подпоследовательность.

ВАРИАНТ 5

Необходимые задачи таковы:

1. обеспечить для контейнера возможность чтения из файла и записи в файл с использованием итераторов потоков.

2. обеспечить для контейнера сортировку по фамилии автора (это должна быть сортировка по умолчанию), а также сортировку по числу выдач.

3. вынести в отдельный вектор все пособия, начиная с пособия с числом выдач больше 4

4. сохранить все имена студентов из вектора шага 3 в новом векторе.

5. найти число студентов с именами идущими по алфавиту за заданным в векторе, полученном на шаге 4.

6. найти среднее число выдач по всем пособиям с использованием accumulate()

7. проверить, входит ли вектор, полученный на шаге 3, в исходный вектор как подпоследовательность.

ВАРИАНТ 6

Необходимые задачи таковы:

1. обеспечить для контейнера возможность чтения из файла и записи в файл с использованием итераторов потоков.

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

3. вынести в отдельный вектор все книги, начиная с книги, первый автор которой написал более 4 книг

4. сохранить все имена авторов из вектора шага 3 в новом векторе.

5. найти число авторов с именами идущими по алфавиту за заданным в векторе, полученном на шаге 4.

6. найти среднее число авторов для всех книг с использованием accumulate()

7. проверить, входит ли вектор, полученный на шаге 3, в исходный вектор как подпоследовательность.

ВАРИАНТ 7

Необходимые задачи таковы:

1. обеспечить для контейнера возможность чтения из файла и записи в файл с использованием итераторов потоков.

2. обеспечить для контейнера сортировку по названию группы (это должна быть сортировка по умолчанию), а также сортировку по среднему баллу для всех студентов.

3. вынести в отдельный вектор все группы, начиная с группы, число студентов которой больше 4

4. сохранить все имена студентов из вектора шага 3 в новом векторе.

5. найти число студентов с именами идущими по алфавиту за заданным в векторе, полученном на шаге 4.

6. найти средний балл для всех групп с использованием accumulate()

7. проверить, входит ли вектор, полученный на шаге 3, в исходный вектор как подпоследовательность.

ВАРИАНТ 8

Необходимые задачи таковы:

1. обеспечить для контейнера возможность чтения из файла и записи в файл с использованием итераторов потоков.

2. обеспечить для контейнера сортировку по названию университета (это должна быть сортировка по умолчанию), а также сортировку по общему числу студентов.

3. вынести в отдельный вектор все университеты, начиная с университета с числом факультетов, больше чем 4

4. сохранить все названия факультетов из вектора шага 3 в новом векторе

5. найти число факультетов с именами идущими по алфавиту за заданным в векторе, полученном на шаге 4.

6. найти среднее число студентов для всех университетов с использованием accumulate()

7. проверить, входит ли вектор, полученный на шаге 3, в исходный вектор как подпоследовательность.

ВАРИАНТ 9

Необходимые задачи таковы:

1. обеспечить для контейнера возможность чтения из файла и записи в файл с использованием итераторов потоков.

2. обеспечить для контейнера сортировку по названию курса (это должна быть сортировка по умолчанию), а также сортировку по общему числу студентов, посетивших курс

3. вынести в отдельный вектор все курсы, начиная с курса, для которого отмечена 100% посещаемость

4. сохранить все названия групп из вектора шага 3 в новом векторе.

5. найти число групп с именами идущими по алфавиту за заданным в векторе, полученном на шаге 4.

6. найти среднее число студентов для всех курсов с использованием accumulate()

7. проверить, входит ли вектор, полученный на шаге 3, в исходный вектор как подпоследовательность.

ВАРИАНТ 10

Необходимые задачи таковы:

1. обеспечить для контейнера возможность чтения из файла и записи в файл с использованием итераторов потоков.

2. обеспечить для контейнера сортировку по названию корпуса (это должна быть сортировка по умолчанию), а также сортировку по числу свободных аудиторий.

3. вынести в отдельный вектор все корпуса, начиная с корпуса, в котором более 4 аудиторий

4. сохранить все номера аудиторий из вектора шага 3 в новом векторе.

5. найти число аудиторий с номерами больше заданного в векторе, полученном на шаге 4.

6. найти среднее число аудиторий для всех корпусов с использованием accumulate()

7. проверить, входит ли вектор, полученный на шаге 3, в исходный вектор как подпоследовательность.

5 Работа с элементами стандартной библиотеки С++

ЧАСТЬ 1. ПРЕДВАРИТЕЛЬНЫЕ СВЕДЕНИЯ

Необходимые сведения о доступе к элементам объектов классов.

В данной работе может возникнуть необходимость получить доступ к элементу объекта класса, на который указывает указатель, т.е. сделать что-то типа

(*p).elem

где p - указатель на класс, содержащий элемент elem.

Для этого есть более простая запись:

указатель->элемент

т.е. в нашем случае можно написать:

p->elem

1. Основные компоненты стандартной библиотеки С++.

Стандартная библиотека C++ состоит из компонент трех основных видов:

1. контейнеров (классов, которые могут содержать данные различных типов, например, vector)

2. итераторов (классов, необходимых для перемещения по контейнерам и получения доступа к текущим элементам в них, подобно тому, как мы перемещаемся по массиву с помощью указателей)

3. алгоритмов, которые производят действия над контейнерами или их элементами, получив к ним доступ через итераторы, например, sort() или reverse().

2. Итераторы.

Важнейшей концепцией стандартной библиотеки С++ является концепция

итераторов.

Итераторы похожи на указатели, с помощью которых производится перемещение по массиву. Для них допустимы в первую очередь операции ++ (перемещается на следующий элемент), сравнения ==, != и операция * (разыменование), которое дает элемент контейнера. Различают ряд классов итераторов:

- входные и выходные (input, output) итераторы (допустимы только указанные операторы, input не может писать через разыменование, output не может читать),

- однонаправленные (могут записывать и считывать данные)

- двунаправленные (добавляется еще операция --),

- итераторы с произвольным доступом (+=, -=, +, -, сравнения на

больше-меньше).

Разные контейнеры определяют итераторы разного класса, то, какой класс итераторов может предоставить контейнер, является свойством этого контейнера. Например, vector или string предоставляют итераторы с произвольным доступом, а list (список) - только двунаправленные.

Тип контейнера (vector<int> или vector<string>) предоставляет итераторы, описывая их типы внутри себя как тип-контейнера::тип-итератора где тип-итератора может быть iterator, reverse_iterator, const_iterator и const_reverse_iterator. Далее можно будет определять переменные такого типа и перемещаться по экземплярам контейнера.

Возьмем контейнер vector<int>. Вот какие итераторы он может предоставлять:

- vector<int>::iterator - итератор, реализующий прямой проход по контейнеру (именно такие итераторы возвращаются begin() и end())

- vector<int>::reverse_iterator - итератор, реализующий обратный проход по контейнеру (такие итераторы возвращаются rbegin() и rend())

- vector<int>::const_iterator - итератор, через который нельзя менять элементы контейнера (есть и const_reverse_iterator, но в Visual C++ он не работает).

Непосредственно как объект итератор внутри контейнера не содержится, там описан только его тип. Переменную-итератор нужно определять отдельно:

vector<int>::iterator i;

Раз определенный, такой итератор может перемещаться по разным экземплярам вектора целых.

Вот как работать с итераторами для вектора:

vector<int>v; // определяем вектор

// заполняем вектор

// определяем итератор для перемещения по этому вектору

vector<int>::iterator i;

// v.begin() возвращает итератор, указывающий на начало вектора

// v.end() - указывающий за конец вектора

// i++ продвигает итератор на следующий элемент

for (i = v.begin(); i != v.end(); i++) {

cout << *i; // *i возвращает элемент вектора, на который

// ссылается итератор

*i = 200; // изменяем значение через итератор

}

iterator и reverse_iterator являются итераторами одного класса (для вектора - итераторами с произвольным доступом), они только по-разному интерпретируют последовательность. Вот как работает reverse_iterator:

vector<int>::reverse_iterator ri;

// v.rbegin() возвращает итератор для обратного прохода,

// указывающий на конец вектора

// v.rend() - указывает на позицию перед началом вектора

// это - конец последовательности для обратного итератора

// ri++ продвигает итератор на предыдующий элемент

for (ri = v.rbegin(); ri != v.rend(); ri++)

cout << *i; // проход от конца в начало

Если в векторе были данные 1 2 3, то программа выдаст 3 2 1

const_iterator работает аналогично обычному, за исключением того, что через него нельзя менять элементы массива.

vector<int>::const_iterator ci = v.begin();

*ci = 200; // ошибка

Как мы уже видели, стандартные алгоритмы (такие, как sort() или reverse()) работают с последовательностями, заданными двумя итераторами - указывающим на первый элемент последовательности и за последний.

sort(v.begin(), v.end());

iterator - тип, который описан внутри класса vector. В Visual C++ для того, чтобы можно было описывать переменные такого типа, ошибочно необходимо, помимо

using std::vector;

подключать конкретную специализацию параметризованного класса так:

using std::vector<int>;

Это не требуется по стандарту С++, более того, это даже запрещено. Учитывая эти сложности, можно до улучшения ситуации использовать просто

using namespace std;

3. Ассоциативные массивы.

В стандартной библиотеке С++, помимо векторов, есть ряд других классов контейнеров. Одним из наиболее важных является ассоциативный массив - массив, который индексируется не идущими последовательно целыми числами (как обычный массив), а произвольными данными (чаще всего строками). Такой массив состоит из набора пар "ключ-значение", и если ключи в массиве не повторяются, то любое значение всегда можно получить по соответствующему ключу как

имя-массива[ключ].

Для реализации такого массива в библиотеке задано два класса: map и multimap. Они отличаются тем, что в контейнере map ключи должны быть уникальными, а в контейнере multimap - нет (ключи могут повторяться).

Для работы с map и multimap необходимо подключать заголовочный файл

<map> и пространство имен std.

Сначала рассмотрим класс map.

3.1. Класс map

Класс map, как и vector, является параметризованным, при его задании в угловых скобках нужно задать два параметра - тип ключа и тип значения:

map<string,int> m; // индексируется строками

map<int,int> mi; // индексируется произвольно расположенными целыми

Дальнейшая работа достаточно проста. Для задания элемента массива достаточно простого присваивания:

m["key1"] = 100;

После этого в массиве появляется пара значений "key1" => 100

mi[-2] = 100; // пара -2 => 100

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

int i = m["key1"];

Следует отметить, что если ключ не найден, то в массив помещается пара "ключ" - "значение по умолчанию для типа значения". Для целочисленных типов таким значением считается 0.

Таким образом, если в m есть пара с ключом "key1", то i будет присвоено значение, соответствующее этому ключу, а если нет - в массив будет добавлена пара "key1" => 0 и i присвоится 0.

Это далеко не всегда удобно, как этого избежать - мы увидим чуть позже.

А пока вкратце остановимся на реализации map. Для примера возьмем map<string,int>. Такой массив не содержит отдельно ключи, а отдельно значения, он содержит экземпляры класса pair<string,int>, определенного в стандартной библиотеке. Этот класс содержит два элемента - first и second. Тип first будет string, а second - int, first будет соответствовать ключу, а second - значению. Поместить новую пару элементов в массив можно еще и так:

m.insert(make_pair(string("key1"),100));

где make_pair() - стандартная функция, создающая пару pair из двух

элементов, которые переданы в нее как аргументы.

Рассмотрим, как обойти обязательную вставку элемента при доступе по индексу. Если мы хотим просто проверить наличие ключа в массиве без обязательного присваивания, то, если нам важен только сам факт наличия элемента с данным ключом, нужно воспользоваться методом count(), который возвращает число вхождений пары элементов с данным ключом (для

map count() вернет 0 или 1, для multimap - число вхождений может быть

больше 1).

if (m.count("key1") != 0) {

// элемент с данным ключом есть в массиве

}

Если же нам нужно получить значение по ключу, но мы хотим избежать вставки, нам нужно воспользоваться функцией-элементом find(), который возвращает итератор, который указывает на найденный элемент (объект типа pair), если он есть или который равен итератору, который

возвращает end(), если элемент с заданным ключом не найден:

map<string,int>::iterator mi;

if ((mi = m.find("key1")) != m.end()) {

// элемент с данным ключом есть в массиве

// ключ этого элемента даст mi->first, значение - mi->second

cout << "значение=" << mi->second << endl;

}

С другой стороны, автоматическая вставка при индексации может быть и весьма удобной, например, этот эффект дает возможность написать так:

m["key1"]++;

не заботясь о том, есть ли в данный момент в массиве элемент с ключом key1 или нет:

- если он есть - соответствующее ему значение увеличится на 1 (это может быть, например, число вхождений данного слова),

- если его нет - попытка получить значение с тем, чтобы применить к нему инкремент приведет к вставке нового элемента с данным ключом и значением 0, так что инкремент будет тут же применен к только что вставленному элементу и значение станет 1.

Вот так производится обход ассоциативного массива:

map<string,int>::iterator wi;

for (wi = m.begin(); wi != m.end(); wi++)

cout << "key=" << wi->first << " value=" << wi->second << endl;

Итераторы для map являются двунаправленными, для них, например, не определены операции - или +=.

3.2. Класс multimap.

multimap отличается от map тем, что значение ключа в нем может повторяться. Для него не задана операция индексации [] и для получения всех элементов с данным ключом необходимо применять функции-элементы equal_range(), lower_bound() и upper_bound() equal_range() принимает значение ключа и возвращает pair из двух итераторов, первый из которых указывает на первый элемент с заданным ключом (то же возвращает lower_bound()), а второй - после последнего элемента (то же возвращает upper_bound()). Вот так можно вывести все значения для данного ключа:

multimap<string,int> mm;

multimap<string,int>::iterator mi;

string key = "key1";

cout << "key=" << key;

for (mi = mm.lower_bound(key); mi != mm.upper_bound(key); mi++)

cout << "value=" << mi->second << endl;

Помещать элементы в multimap можно только по insert() (которая реализована точно так же, как и для map). Функция-элемент count() может вернуть значение > 1.

3.3. Случай множества значений, соответствующих одному ключу.

Часто встречается ситуация, когда одному ключу соответствует множество значений, например, когда нужно сохранить набор синонимов для данного слова. Есть два подхода к этой ситуации:

1) использовать map, для которого типом значения является вектор

map<string, vector<string> > и накапливать нужные значения в этом векторе

2) использовать multimap - массив с повторяющимися ключами multimap<string, string> накапливать нужные значения как разные значения для одинаковых ключей и пользоваться для доступа к ним функциями-элементами типа equal_range()

4. Множества (set и multiset)

Множества до определенной степени похожи на ассоциативные массивы, но они содержат только ключи и не содержат значений. Их можно рассматривать как автоматически сортируемый набор элементов, в котором элементы не индексируются, но всегда можно проверить, есть ли элемент в наборе или его там нет.

Есть два вида множеств: те, которые могут содержать только уникальные элементы (тип set) и те, в которых элементы могут повторяться (тип multiset). Мы будем рассматривать только тип set.

Для работы с множествами нужно подключать заголовочный файл <set>. При определении переменных типа множество нужно указывать только один тип - тип элемента множества:

set<string> words;

Добавление элементов производится с помощью функции-элемента insert():

words.insert("word1");

words.insert("word2");

Определение числа вхождений элемента и поиск элемента аналогичны map:

if (words.count("word1") != 0) {

// элемент во множестве есть

}

set<string>::iterator si;

if ((si = words.find("word1")) != words.end()) {

// данный элемент есть во множестве

cout << "элемент=" << *si << endl;

}

// вывод множества

for (si = words.begin(); si != words.end(); si++)

cout << *si << " ";

ЗАДАНИЯ НА ЛАБОРАТОРНУЮ РАБОТУ

ВАРИАНТ 1

Разработать программу, которая считывает текст и подсчитывает число повторений каждого слова, за исключением слов, указанных в списке. Слова в тексте разделяются пробелами. Подсчет числа повторений слова реализовывать с помощью ассоциативного массива, список слов для исключения реализовывать с помощью множества. Каждый раз, когда встретилось слово из списка, выводить сообщение. Создать набор функций для вывода результатов и вынести их в отдельный файл. Объявления функций вынести в заголовочный файл. Исходные данные считать из файла и вывести в файл.

ВАРИАНТ 2

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

Объявления функций вынести в заголовочный файл. Исходные данные считать из файла и вывести в файл.

ВАРИАНТ 3

Разработать программу, которая считывает текст и сохраняет номера всех позиций в строках, в которых встретилось данное слово, за исключением слов, указанных в списке. Слова в тексте разделяются пробелами. Накопление позиций в строках реализовывать с помощью ассоциативного массива (map или multimap), список слов для исключения - с помощью множества. Каждый раз, когда встретилось слово из списка, выводить сообщение. Если слово встретилось в одной и той же позиции дважды - выводить сообщение. Создать набор функций для вывода результатов и вынести их в отдельный файл. Объявления функций вынести в заголовочный файл. Исходные данные считать из файла и вывести в файл.

----- вход

уж небо осенью дышало

уж реже солнышко блистало

короче становился день

лесов таинственная сень

-----

список исключения (день осенью лесов короче Пушкин)

----- выход

осенью - из списка

короче - из списка

день - из списка

лесов - из списка

уж - дважды в позиции 1

уж -> 1 1

небо -> 4

осенью -> 9

и т.д.

ВАРИАНТ 4

Разработать программу, которая считывает текст и вычисляет среднюю частоту вхождения каждого слова, за исключением слов, указанных в списке (число вхождений / общее число слов кроме слов из списка). Слова в тексте разделяются пробелами. Подсчет частоты вхождения реализовывать с помощью ассоциативного массива, список слов для исключения реализовывать с помощью множества. Каждый раз, когда встретилось слово из списка, выводить сообщение. Создать набор функций для вывода результатов и вынести их в отдельный файл. Объявления функций вынести в заголовочный файл. Исходные данные считать из файла и вывести в файл.

ВАРИАНТ 5

Разработать программу, которая считывает текст и вычисляет отношение длины каждого слова к средней длине слов текста, за исключением слов, указанных в списке (длина / средняя длина слов, не учитывая слова из списка). Слова в тексте разделяются пробелами. Подсчет относительной длины реализовывать с помощью ассоциативного массива, список слов для исключения реализовывать с помощью множества. Каждый раз, когда встретилось слово из списка, выводить сообщение. Создать набор функций для вывода результатов и вынести их в отдельный файл. Объявлени функций вынести в заголовочный файл. Исходные данные считать из файла и вывести в файл.

ВАРИАНТ 6

Разработать программу, которая считывает текст и вычисляет среднюю частоту встречаемости букв в данном тексте, за исключением слов, указанных в списке (число вхождений буквы / длину текста без длины слов из списка). Слова в тексте разделяются пробелами (при подсчете частоты пробелы не учитывать). Подсчет частоты реализовывать с помощью ассоциативного массива, список слов для исключения реализовывать с помощью множества. Каждый раз, когда встретилось слово из списка, выводить сообщение. Создать набор функций для вывода результатов и вынести их в отдельный файл. Объявления функций вынести в заголовочный файл. Исходные данные считать из файла и вывести в файл.

ВАРИАНТ 7

Разработать программу, которая считывает текст и подсчитывает число слов на данную букву в этом тексте, за исключением слов, указанных в списке. Слова в тексте разделяются пробелами. Подсчет числа слов реализовывать с помощью ассоциативного массива (map или multimap), список слов для исключения реализовывать с помощью множества. Буквы должны идти в списке в порядке расположения в тексте слов, которые с них начинаются. Каждый раз, когда встретилось слово из списка исключений, выводить сообщение. Вместе с числом слов выводить и список этих слов. Создать набор функций для вывода результатов и вынести их в отдельный файл. Объявления функций вынести в заголовочный файл. Исходные данные считать из файла и вывести в файл.

----- вход

уж небо осенью дышало

уж реже солнышко блистало

короче становился день

лесов таинственная сень

-----

список исключения (день осенью лесов короче Пушкин)

----- выход

осенью - из списка

короче - из списка

день - из списка

лесов - из списка

у -> уж уж (2)

н -> небо (1)

д -> дышало (1)

р -> реже (1)

с -> солнышко становился сень (3)

б -> блистало (1)

т -> таинственная (1)

ВАРИАНТ 8

Разработать программу, которая считывает текст, находит число всех слов, начинающихся на ту же букву, с которой начинается данное слово. Слова в тексте разделяются пробелами. Слова из списка исключения не учитывать. Если ни одного слова на ту же букву нет - данные по слову не выводить. Подсчет числа слов реализовывать с помощью ассоциативного массива (map или multimap), список слов для исключения реализовывать с помощью множества. Каждый раз, когда встретилось слово из списка исключений, выводить сообщение. Вместе с числом слов выводить и список этих слов. Создать набор функций для вывода результатов и вынести их в отдельный файл. Объявления функций вынести в заголовочный файл.

Наши рекомендации