Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
МУ7,8 (2011).doc
Скачиваний:
0
Добавлен:
01.05.2025
Размер:
208 Кб
Скачать
☆

Задание 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. Кроме этого, программа должна подсчитать общее количество слов в тексте.

На примере этого задания мы показываем технологию разработки многофайловых проектов.

Алгоритм решения задачи не представляет особой сложности:

  1. Открыть входной файл.

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

  1. Для каждой строки 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. Не терпеть и малого своего недостатка - вот признак совершенства. От изъянов духовных и телесных редко кто свободен, но часто их лелеют, когда от них легко бы исцелиться. Вчуже досадно видеть разумному, как ничтожный изъян порой портит великолепное сочетание достоинств, - довольно и облачка, чтобы затмить солнце. Родимые пятна на доброй славе злоба людская сразу подметит - и упорно в них метит. Особенно ценно искусство скрывать свой недостаток, обращая его в преимущество. Так, Цезарь скрывал свою плешь лавровым венком.

Протестируйте эту программу на других текстах.

Давайте повторим наиболее важные моменты этой лабораторной работы.

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

  2. Интерфейс грамотно написанной функции определяется ее заголовком.

  3. Для вызова функции надо указать ее имя и набор аргументов.

  4. В определении, в объявлении и при вызове функции типы и порядок следования аргументов и параметров должны совпадать.

  5. Передача параметров в функцию может выполняться по значению или по адресу.

  6. Входные данные функции надо передавать по значению или по константной ссылке, результаты ее работы – через возвращаемое значение, а при необходи­мости передать более одной величины – через параметры по ссылке или указа­телю.

  7. При написании функции нужно предусмотреть все возможные ошибки и обеспечить пользователя функции средствами их диагностики.

  8. Печать диагностических сообщений внутри функции нежелательна.

  9. Функция может иметь несколько параметров со значениями по умолчанию. Они должны находиться в конце списка параметров.

  1. Массивы всегда передаются в функцию по адресу. Количество элементов в мас­сиве должно передаваться отдельным параметром.

  1. Рекурсивная функция должна содержать хотя бы одну нерекурсивную ветвь. При использовании рекурсии следует учитывать возникающие при этом проб­лемы и накладные расходы.

  2. В многофайловых проектах важно грамотно разбить задачу на подзадачи и распределить функции по файлам.

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