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

Задание 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. Для предотвращения ошибок компиляции, связанных с повторным включе­нием заголовочных файлов, следует использовать так называемые стражи вклю­чения.