
- •«Технология программирования»
- •«Функции», «Перегрузка и шаблоны функций»
- •Самара 2007
- •Лабораторная работа №7 функции
- •Краткие сведения из теории
- •Описание лабораторной установки и по
- •Указания к выполнению работы
- •Задание 3. Рекурсивные функции
- •Контрольные вопросы
- •Лабораторная работа №8 перегрузка и шаблоны функций
- •Краткие сведения из теории
- •Указания к выполнению работы
- •Контрольные вопросы
- •Приборы и программное обеспечение
- •Содержание отчета
- •Библиографический список
Задание 3. Рекурсивные функции
Написать программу упорядочивания массива методом быстрой, сортировки, используя рекурсию.
Пришло время показать рекурсивную реализацию метода быстрой сортировки. Говоря кратко, рекурсивной называется функция, в которой имеется обращение к ней самой.
К счастью, любая функция в программе на C++ может вызываться рекурсивно. При этом в стеке выделяется новый участок памяти для размещения копий параметров, а также автоматических и регистровых переменных, поэтому предыдущее состояние выполняемой функции сохраняется, и к нему впоследствии можно вернуться (так и произойдет, если только ваша программа где-нибудь не зависнет).
Одна из возможных версий программы сортировки приведена ниже.
#include <iostream.h>
void qsort(float* array, int left, int right);
int main() {
const int n = 10;
float arr[n];
int i, l, r;
cout << "Введите элементы массива: ";
for (i = 0; i < n; i++) cin >> arr[i];
l = 0; r = n - 1; // левая и правая границы начального фрагмента
qsort(arr, I, r); II 1
for (i = 0; i < n: i++) cout << arr[i] << ' ';
return 0;
}
void qsort (float* array, int left, int right) {
int i = left, j = right;
float middle = array[(left + right) / 2];
float temp;
while (i < j) {
while (array[i] < middle) i++;
while (middle < array[j]) j--;
if (i <= j) {
temp = array[i];
array[i] = array[j];
array[j] = temp;
i++;
j--;
}
}
if (left < j) qsort(array, left, j); // 2
if (i < right) qsort(array, i, right); // 3
}
Процедура разделения реализована здесь в виде рекурсивно вызываемой функции qsort(), в теле которой есть два обращения к самой себе: в операторе 2 – для сортировки левой половинки текущего фрагмента, и в операторе 3 – для сортировки его правой половинки.
Однако, у рекурсии есть и недостатки: во-первых, такую программу труднее отлаживать, поскольку требуется контролировать глубину рекурсивного обращения, во-вторых, при большой глубине стек может переполниться, а в-третьих, использование рекурсии повышает накладные расходы (например, в данном случае в стеке сохраняются отнюдь не два числа, представляющие собой границы фрагмента, а гораздо больше, не говоря уже о затратах, связанных с вызовом функции). Поэтому рекурсию при всей ее красоте следует применять с осторожностью.
Задание 4. Многофайловый проект – форматирование текста
Написать программу форматирования текста, читаемого из файла unformt.txt и состоящего из строк ограниченной длины. Слова в строке разделены произвольным количеством пробелов. Программа должна читать входной файл по строкам, форматировать каждую строку и выводить результат в выходной файл formatd.txt. Форматирование заключается в выравнивании границ текста слева и справа путем равномерного распределения пробелов между соседними словами, а также в отступе с левой стороны страницы на margin позиций, то есть результирующий текст должен находиться в позициях margin + 1 .. margin + maxljine. Кроме этого, программа должна подсчитать общее количество слов в тексте.
На примере этого задания мы показываем технологию разработки многофайловых проектов.
Алгоритм решения задачи не представляет особой сложности:
Открыть входной файл.
Читать файл построчно в текстовый буфер line, попутно удаляя возможные пробелы в начале строки (до первого слова).
Для каждой строки line выполнить следующие действия:
• Вычислить величину интервала (количество пробелов), которую необходимо обеспечить между соседними словами для равномерного распределения слов в пределах строки.
• Вывести каждое слово из строки line в выходной файл, вставляя между словами необходимое количество пробелов и одновременно увеличивая счетчик слов на единицу.
4. После обработки последней строки входного файла вывести на экран значение счетчика слов и закрыть выходной файл.
Разбиение на подзадачи.
В результате детализации описанного алгоритма определяем спецификации нужных нам функций:
void Deflnter (const char* pline, int & base_int, int & add_int, int & inter) определяет для строки, на которую указывает pline, количество межсловных промежутков inter, требуемую величину основного интервала base_int для каждого промежутка (количество пробелов) и величину дополнительного интервала add_int, определяемую как остаток от деления общего количества пробелов в строке на количество межсловных промежутков; последняя величина должна быть равномерно распределена путем добавления одного пробела в каждый из первых add_int промежутков;
void GetLine (FILE* finp, char* pline) читает очередную строку из входного файла в массив символов с адресом pline, ликвидируя при этом пробелы в начале строки;
void PutInterval (FILE* fout, const int k) выводит очередной интервал, состоящий из k пробелов;
int PutWord (FILE* fout, const char* pline, const int startpos) выводит очередное слово в выходной файл, начиная с позиции startpos текущей строки pline; возвращает номер позиции в строке pline, следующей за последним переданным символом, или 0 – если достигнут конец строки;
int SearchNextWord (const char* pline, const int curpos) возвращает номер позиции, с которой начинается следующее слово в строке pline, или 0, если достигнут конец строки (поиск начинается с позиции curpos).
Разбиение на модули.
Наша программа будет располагаться в двух исходных файлах: task7_4.cpp – с функцией main, edit.cpp – с реализацией перечисленных выше функций, а также заголовочный файл edit.h с интерфейсом этих функций.
Ниже приводится содержимое этих файлов.
//////////////////////////////////////////////////////////////////////////////////
// Файл Task7_4.cpp
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "edit.h"
// Глобальные переменные
const int maxl_line = 63;
const int margin = 5;
int main ()
{
FILE* finp;
FILE* fout;
char line[maxl_line + 1];
int b_i, a_i, start, next, inter;
int nword = 0;
printf("Paбoтaeт программа Task7_4.\n");
if(!(finp = fopen("unformt.txt", "r"))) {
printf(“Файл unformt.txt не найден.\n"); exit(0);
}
printf("Читается файл unformt.txt.\n");
if(!(fout = fopen("formatd.txt", "w")))
{
pгintf("Фaйл formatd.txt не создан.\n"); exit(0);
}
printf("Выполняется запись в файл formatd.txt.\n");
while(GetLine(finp, line))
{
Deflnter(line, b_i, a_i, inter);
PutInterval(fout, margin);
next = PutWord(fout, line, 0, nword);
for (int i = 0; i < inter; i++)
{
start = SearchNextWord(line, next);
Putlnterval (fout, b_i);
if (a_i)
{
a_i--;
Putlnterval (fout, 1);
}
next = PutWord(fout, line, start, nword);
if (!next) break;
}
fprintf(fout, "\n");
}
printf("\nKoличecтвo слов - %d\n", nword);
fclose(fout);
printf("Paбoтa завершена.\n");
return 0;
}
////////////////////////////////////////////////////////////////////////////////////////
// Файл Edit.h
// Прототипы функций
void Deflnter(const char* pline, int& base_int, int& add_int, int& inter);
int GetLine(FILE*, char*);
void Putlnterval(FILE*, const int);
int PutWord(FILE*, const char*, const int, int&);
int SearchNextWord(const char*, const int);
// Глобальные переменные
extern const Int maxljine;
///////////////////////////////////////У///////////////////
// Файл Edlt.cpp
#include <stdio.h>
#include <string.h>
#include "edit.h"
int GetLine(FILE* finp, char* pline)
{
int i = 0;
char c;
while ((c = fgetc(finp)) == ' ' ) i++;
if(c == EOF) return 0;
fseek (finp, -1, SEEK_CUR);
fgets( pline, maxl_line - i + 1, finp);
pline[strlen(pline) - 1] = 0;
return 1;
}
int SearchNextWord(const char* pline, const int curpos)
{
int i = curpos;
while(pline[i] !=' ')
{
if (pline[i] == '\n') return 0;
i++;
}
while (pline[i] == ‘ ‘ && pline[i + 1] == ‘ ') i++;
return i+1;
}
void DefInter(const char* pline, int& base_int, int& add_int, int& inter)
{
int k = 0, end;
end = strlen(pline) - 1;
while ((pline[end] == ' ') || (pline[end] == '\n') || (pline[end] == '\r')) end--;
inter = 0;
for (unsigned int i = 0; i < end; i++)
{
if (pline[i] ==’ ')
{
k++;
if (pline[i +1] != ' ') inter++;
}
}
int blank_amount = k + maxl_line - end;
if (!k)
{
base_int = 0;
add_int = 0; }
else
{
base_int = blank_amount / inter;
add_int = blank_amount % inter;
}
return;
}
int PutWord (FILE* fout, const char* pline, const int startpos, int& n)
{
int i = startpos;
char c;
n++;
while ((c = pline[i++]) != ' ')
{
fprintf(fout, "%c", c);
if ((c == '\n') || (c == '\0')) { i = 0; break; }
}
return i – 1;
}
void Putlnterval(FILE* fout, const int k)
{
for (int i = 0; i < k; i++) fprintf(fout, " ");
return;
}
////////////////////////////////////////////////////////////////////////////////
Обратите внимание, что имена функций мы здесь записали (для разнообразия) в стиле Microsoft, с использованием прописных букв для выделения осмысленных частей имени. Константу maxline следует задавать большей, чем максимальная длина строки исходного файла. В качестве самостоятельного упражнения измените программу так, чтобы можно было форматировать текст и в более узкую колонку, чем в исходном файле.
Приведем результаты тестирования этой программы. Содержимое входного файла unformt.txt:
23. Не терпеть и малого своего недостатка - вот признак совершенства. От изъянов духовных и телесных редко кто свободен, но часто их лелеют, когда от них легко бы исцелиться. Вчуже досадно видеть разумному, как ничтожный изъян порой портит великолепное сочетание достоинств, - довольно и облачка, чтобы затмить солнце. Родимые пятна на доброй славе злоба людская сразу подметит - и упорно в них метит. Особенно ценно искусство скрывать свой недостаток, обращая его в преимущество. Так, Цезарь скрывал свою плешь лавровым венком.
Содержимое выходного файла formatd.txt:
23. Не терпеть и малого своего недостатка - вот признак совершенства. От изъянов духовных и телесных редко кто свободен, но часто их лелеют, когда от них легко бы исцелиться. Вчуже досадно видеть разумному, как ничтожный изъян порой портит великолепное сочетание достоинств, - довольно и облачка, чтобы затмить солнце. Родимые пятна на доброй славе злоба людская сразу подметит - и упорно в них метит. Особенно ценно искусство скрывать свой недостаток, обращая его в преимущество. Так, Цезарь скрывал свою плешь лавровым венком.
Протестируйте эту программу на других текстах.
Давайте повторим наиболее важные моменты этой лабораторной работы.
Функция – это именованная последовательность операторов, выполняющая законченное действие. Функции нужны для упрощения структуры программы.
Интерфейс грамотно написанной функции определяется ее заголовком.
Для вызова функции надо указать ее имя и набор аргументов.
В определении, в объявлении и при вызове функции типы и порядок следования аргументов и параметров должны совпадать.
Передача параметров в функцию может выполняться по значению или по адресу.
Входные данные функции надо передавать по значению или по константной ссылке, результаты ее работы – через возвращаемое значение, а при необходимости передать более одной величины – через параметры по ссылке или указателю.
При написании функции нужно предусмотреть все возможные ошибки и обеспечить пользователя функции средствами их диагностики.
Печать диагностических сообщений внутри функции нежелательна.
Функция может иметь несколько параметров со значениями по умолчанию. Они должны находиться в конце списка параметров.
Массивы всегда передаются в функцию по адресу. Количество элементов в массиве должно передаваться отдельным параметром.
Рекурсивная функция должна содержать хотя бы одну нерекурсивную ветвь. При использовании рекурсии следует учитывать возникающие при этом проблемы и накладные расходы.
В многофайловых проектах важно грамотно разбить задачу на подзадачи и распределить функции по файлам.
Для предотвращения ошибок компиляции, связанных с повторным включением заголовочных файлов, следует использовать так называемые стражи включения.