45940 (665245), страница 12

Файл №665245 45940 (Язык С) 12 страница45940 (665245) страница 122016-07-31СтудИзба
Просмтор этого файла доступен только зарегистрированным пользователям. Но у нас супер быстрая регистрация: достаточно только электронной почты!

Текст из файла (страница 12)

CHAR). Объект может быть фактической переменной, массивом и

структурой, или именем основного типа, как INT или DOUBLE,

или именем производного типа, как структура. В нашем случае

число ключевых слов равно размеру массива, деленному на раз-

мер одного элемента массива. Это вычисление используется в

утверждении #DEFINE для установления значения NKEYS:

#DEFINE NKEYS (SIZEOF(KEYTAB) / SIZEOF(STRUCT KEY))

Теперь перейдем к функции GETWORD. Мы фактически написа-

ли более общий вариант функции GETWORD, чем необходимо для

этой программы, но он не на много более сложен. Функция

GETWORD возвращает следующее “слово” из ввода, где словом

считается либо строка букв и цифр, начинающихся с буквы, ли-

бо отдельный символ. Тип объекта возвращается в качетве зна-

чения функции; это - LETTER, если найдено слово, EOF для

конца файла и сам символ, если он не буквенный.

GETWORD(W, LIM) /* GET NEXT WORD FROM INPUT */

CHAR *W;

INT LIM;

\(

INT C, T;

IF (TYPE(C=*W++=GETCH()) !=LETTER) \(

*W='\0';

RETURN©;

\)

  • 136 -

WHILE (--LIM > 0) \(

T = TYPE(C = *W++ = GETCH());

IF (T ! = LETTER && T ! = DIGIT) \(

UNGETCH©;

BREAK;

\)

\)

*(W-1) - '\0';

RETURN(LETTER);

\)

Функция GETWORD использует функции GETCH и UNGETCH, которые

мы написали в главе 4: когда набор алфавитных символов пре-

рывается, функция GETWORD получает один лишний символ. В ре-

зультате вызова UNGETCH этот символ помещается назад во ввод

для следующего обращения.

Функция GETWORD обращается к функции TYPE для определе-

ния типа каждого отдельного символа из файла ввода. Вот ва-

риант, справедливый только для алфавита ASCII.

TYPE© /* RETURN TYPE OF ASCII CHARACTER */ INT C;

\(

IF (C>= 'A' && C= 'A' && C<= 'Z')

RETURN(LETTER);

ELSE IF (C>= '0' && C<= '9')

RETURN(DIGIT);

ELSE

RETURN©;

\)

Символические константы LETTER и DIGIT могут иметь любые

значения, лишь бы они не вступали в конфликт с символами,

отличными от буквенно-цифровых, и с EOF; очевидно возможен

следующий выбор

#DEFINE LETTER 'A'

#DEFINE DIGIT '0'

функция GETWORD могла бы работать быстрее, если бы обращения

к функции TYPE были заменены обращениями к соответствующему

массиву TYPE[ ]. В стандартной библиотеке языка “C” предус-

мотрены макросы ISALPHA и ISDIGIT, действующие необходимым

образом.

Упражнение 6-1.

Сделайте такую модификацию функции GETWORD и оцените,

как изменится скорость работы программы.

Упражнение 6-2.

Напишите вариант функции TYPE, не зависящий от конкрет-

ного наборасимволов.

  • 137 -

Упражнение 6-3.

Напишите вариант программы подсчета ключевых слов, кото-

рый бы не учитывал появления этих слов в заключенных в ка-

вычки строках.

6.4. Указатели на структуры.

Чтобы проиллюстрировать некоторые соображения, связанные

с использованием указателей и массивов структур, давайте

снова составим программу подсчета ключевых строк, используя

на этот раз указатели, а не индексы массивов.

Внешнее описание массива KEYTAB не нужно изменять, но

функции MAIN и BINARY требуют модификации.

MAIN() /* COUNT C KEYWORD; POINTER VERSION */

\(

INT T;

CHAR WORD[MAXWORD];

STRUCT KEY *BINARY(), *P;

WHILE ((T = GETWORD(WORD, MAXWORD;) !=EOF)

IF (T==LETTER)

IF ((P=BINARY(WORD,KEYTAB,NKEYS)) !=NULL)

P->KEYCOUNT++;

FOR (P=KEYTAB; P>KEYTAB + NKEYS; P++)

IF (P->KEYCOUNT > 0)

PRINTF(“%4D %S/N”, P->KEYCOUNT, P->KEYWORD);

\)

STRUCT KEY BINARY(WORD, TAB, N) / FIND WORD */

CHAR WORD / IN TAB[0]...TAB[N-1] */

STRUCT KEY TAB [];

INT N;

\(

INT COND;

STRUCT KEY *LOW = &TAB[0];

STRUCT KEY *HIGH = &TAB[N-1];

STRUCT KEY *MID;

WHILE (LOW <= HIGH) \(

MID = LOW + (HIGH-LOW) / 2;

IF ((COND = STRCMP(WORD, MID->KEYWORD)) < 0)

HIGH = MID - 1;

ELSE IF (COND > 0)

LOW = MID + 1;

ELSE

RETURN(MID);

\)

RETURN(NULL);

\)

Здесь имеется несколько моментов, которые стоит отме-

тить. Во-первых, описание функции BINARI должно указывать,

что она возвращает указатель на структуру типа KEY, а не на

целое; это объявляется как в функции MAIN, так и в BINARY.

Если функция BINARI находит слово, то она возвращает указа-

тель на него; если же нет, она возвращает NULL.

  • 138 -

Во-вторых, все обращения к элементам массива KEYTAB осу-

ществляются через указатели. Это влечет за собой одно сущес-

твенное изменение в функции BINARY: средний элемент больше

нельзя вычислять просто по формуле

MID = (LOW + HIGH) / 2

потому что сложение двух указателей не дает какого-нибудь

полезного результата (даже после деления на 2) и в действи-

тельности является незаконным. эту формулу надо заменить на

MID = LOW + (HIGH-LOW) / 2

в результате которой MID становится указателем на элемент,

расположенный посередине между LOW и HIGH.

Вам также следует разобраться в инициализации LOW и

HIGH. указатель можно инициализировать адресом ранее опреде-

ленного объекта; именно как мы здесь и поступили.

В функции MAIN мы написали

FOR (P=KEYTAB; P < KEYTAB + NKEYS; P++)

Если P является указателем структуры, то любая арифметика с

P учитывает фактический размер данной структуры, так что P++

увеличивает P на нужную величину, в результате чего P указы-

вает на следующий элемент массива структур. Но не считайте,

что размер структуры равен сумме размеров ее членов, - из-за

требований выравнивания для различных объектов в структуре

могут возникать “дыры”.

И, наконец, несколько второстепенный вопрос о форме за-

писи программы. Если возвращаемая функцией величина имеет

тип, как, например, в

STRUCT KEY *BINARY(WORD, TAB, N)

Tо может оказаться, что имя функции трудно выделить среди

текста. В связи с этим иногда используется другой стиль за-

писи:

STRUCT KEY *

BINARY(WORD, TAB, N)

Это главным образом дело вкуса; выберите ту форму, которая

вам нравится, и придерживайтесь ее.

6.5. Структуры, ссылающиеся на себя.

Предположим, что нам надо справиться с более общей зада-

чей, состоящей в подсчете числа появлений всех слов в неко-

тором файле ввода. Так как список слов заранее не известен,

мы не можем их упорядочить удобным образом и использовать

бинарный поиск. Мы даже не можем осуществлять последователь-

ный просмотр при поступлении каждого слова, с тем чтобы ус-

тановить, не встречалось ли оно ранее; такая программа будет

работать вечно. (Более точно, ожидаемое время работы растет

как квадрат числа вводимых слов). Как же нам организовать

программу, чтобы справиться со списком произвольных слов?

  • 139 -

Одно из решений состоит в том, чтобы все время хранить

массив поступающих до сих пор слов в упорядоченном виде, по-

мещая каждое слово в нужное место по мере их поступления.

OДнако это не следует делать, перемещая слова в линейном

массиве, - это также потребует слишком много времени. Вместо

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

ревом.

Каждому новому слову соответствует один “узел” дерева;

каждый узел содержит:

указатель текста слова

счетчик числа появлений

указатель узла левого потомка

указатель узла правого потомка

Никакой узел не может иметь более двух детей; возможно от-

сутсвие детей или наличие только одного потомка.

Узлы создаются таким образом, что левое поддерево каждо-

го узла содержит только те слова, которые меньше слова в

этом узле, а правое поддерево только те слова, которые боль-

ше. Чтобы определить, находится ли новое слово уже в дереве,

начинают с корня и сравнивают новое слово со словом, храня-

щимся в этом узле. Если слова совпадают, то вопрос решается

утвердительно. Если новое слово меньше слова в дереве, то

переходят к рассмотрению левого потомка; в противном случае

исследуется правый потомок. Если в нужном направлении пото-

мок отсутствует, то значит новое слово не находится в дереве

и место этого недостающего потомка как раз и является мес-

том, куда следует поместить новое слово. Поскольку поиск из

любого узла приводит к поиску одного из его потомков, то сам

процесс поиска по существу является рекурсивным. В соответс-

твии с этим наиболее естественно использовать рекурсивные

процедуры ввода и вывода.

Возвращаясь назад к описанию узла, ясно, что это будет

структура с четырьмя компонентами:

STRUCT TNODE \( /* THE BASIC NODE */

CHAR WORD; / POINTS TO THE TEXT */

INT COUNT; /* NUMBER OF OCCURRENCES */

STRUCT TNODE LEFT; / LEFT CHILD */

STRUCT TNODE RIGHT; / RIGHT CHILD */

\);

Это “рекурсивное” описание узла может показаться рискован-

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

имеет права содержать ссылку на саму себя, но

STRUCT TNODE *LEFT;

описывает LEFT как указатель на узел, а не как сам узел.

  • 140 -

Текст самой программы оказывается удивительно маленьким,

если, конечно, иметь в распоряжении набор написанных нами

ранее процедур, обеспечивающих нужные действия. Мы имеем в

виду функцию GETWORD для извлечения каждого слова из файла

ввода и функцию ALLOC для выделения места для хранения слов.

Ведущая программа просто считывает слова с помощью функ-

ции GETWORD и помещает их в дерево, используя функцию TREE.

#DEFINE MAXWORD 20

MAIN() /* WORD FREGUENCY COUNT */

\(

STRUCT TNODE *ROOT, *TREE();

CHAR WORD[MAXWORD];

INT T;

ROOT = NULL;

WHILE ((T = GETWORD(WORD, MAXWORD)) \! = EOF)

IF (T == LETTER)

ROOT = TREE(ROOT, WORD);

TREEPRINT(ROOT);

\)

Функция TREE сама по себе проста. Слово передается функ-

цией MAIN к верхнему уровню (корню) дерева. На каждом этапе

это слово сравнивается со словом, уже хранящимся в этом уз-

ле, и с помощью рекурсивного обращения к TREE просачивается

вниз либо к левому, либо к правому поддереву. В конце концов

это слово либо совпадает с каким-то словом, уже находящимся

в дереве (в этом случае счетчик увеличивается на единицу),

либо программа натолкнется на нулевой указатель, свидетель-

ствующий о необходимости создания и добавления к дереву но-

вого узла. В случае создания нового узла функция TREE возв-

ращает указатель этого узла, который помещается в родитель-

ский узел.

STRUCT TNODE *TREE(P, W)

/* INSTALL W AT OR BELOW P */

STRUCT TNODE *P;

CHAR *W;

\(

STRUCT TNODE *TALLOC();

CHAR *STRSAVE();

INT COND;

IF (P == NULL) \( /* A NEW WORD

HAS ARRIVED */

P == TALLOC(); /* MAKE A NEW NODE */

P->WORD = STRSAVE(W);

P->COUNT = 1;

P->LEFT = P->RIGHT = NULL;

\) ELSE IF ((COND = STRCMP(W, P->WORD)) == 0)

P->COUNT++; /* REPEATED WORD */

ELSE IF (COND < 0)/* LOWER GOES INTO LEFT SUBTREE */

P->LEFT = TREE(P->LEFT, W);

ELSE /* GREATER INTO RIGHT SUBTREE */

P->RIGHT = TREE(P->RIGHT, W);

RETURN(P);

\)

  • 141 -

Память для нового узла выделяется функцией TALLOC, явля-

ющейся адаптацией для данного случая функции ALLOC, написан-

ной нами ранее. Она возвращает указатель свободного прост-

ранства, пригодного для хранения нового узла дерева. (Мы

вскоре обсудим это подробнее). Новое слово копируется функ-

цией STRSAVE в скрытое место, счетчик инициализируется еди-

ницей, и указатели обоих потомков полагаются равными нулю.

Эта часть программы выполняется только при добавлении нового

узла к ребру дерева. Мы здесь опустили проверку на ошибки

возвращаемых функций STRSAVE и TALLOC значений (что неразум-

но для практически работающей программы).

Функция TREEPRINT печатает дерево, начиная с левого под-

дерева; в каждом узле сначала печатается левое поддерево

(все слова, которые младше этого слова), затем само слово, а

затем правое поддерево (все слова, которые старше). Если вы

неуверенно оперируете с рекурсией, нарисуйте дерево сами и

напечатайте его с помощью функции TREEPRINT ; это одна из

наиболее ясных рекурсивных процедур, которую можно найти.

TREEPRINT (P) /* PRINT TREE P RECURSIVELY */

STRUCT TNODE *P;

\(

IF (P != NULL) \(

TREEPRINT (P->LEFT);

PRINTF(“%4D %S\N”, P->COUNT, P->WORD);

Характеристики

Тип файла
Документ
Размер
2,35 Mb
Материал
Тип материала
Учебное заведение
Неизвестно

Список файлов реферата

Свежие статьи
Популярно сейчас
А знаете ли Вы, что из года в год задания практически не меняются? Математика, преподаваемая в учебных заведениях, никак не менялась минимум 30 лет. Найдите нужный учебный материал на СтудИзбе!
Ответы на популярные вопросы
Да! Наши авторы собирают и выкладывают те работы, которые сдаются в Вашем учебном заведении ежегодно и уже проверены преподавателями.
Да! У нас любой человек может выложить любую учебную работу и зарабатывать на её продажах! Но каждый учебный материал публикуется только после тщательной проверки администрацией.
Вернём деньги! А если быть более точными, то автору даётся немного времени на исправление, а если не исправит или выйдет время, то вернём деньги в полном объёме!
Да! На равне с готовыми студенческими работами у нас продаются услуги. Цены на услуги видны сразу, то есть Вам нужно только указать параметры и сразу можно оплачивать.
Отзывы студентов
Ставлю 10/10
Все нравится, очень удобный сайт, помогает в учебе. Кроме этого, можно заработать самому, выставляя готовые учебные материалы на продажу здесь. Рейтинги и отзывы на преподавателей очень помогают сориентироваться в начале нового семестра. Спасибо за такую функцию. Ставлю максимальную оценку.
Лучшая платформа для успешной сдачи сессии
Познакомился со СтудИзбой благодаря своему другу, очень нравится интерфейс, количество доступных файлов, цена, в общем, все прекрасно. Даже сам продаю какие-то свои работы.
Студизба ван лав ❤
Очень офигенный сайт для студентов. Много полезных учебных материалов. Пользуюсь студизбой с октября 2021 года. Серьёзных нареканий нет. Хотелось бы, что бы ввели подписочную модель и сделали материалы дешевле 300 рублей в рамках подписки бесплатными.
Отличный сайт
Лично меня всё устраивает - и покупка, и продажа; и цены, и возможность предпросмотра куска файла, и обилие бесплатных файлов (в подборках по авторам, читай, ВУЗам и факультетам). Есть определённые баги, но всё решаемо, да и администраторы реагируют в течение суток.
Маленький отзыв о большом помощнике!
Студизба спасает в те моменты, когда сроки горят, а работ накопилось достаточно. Довольно удобный сайт с простой навигацией и огромным количеством материалов.
Студ. Изба как крупнейший сборник работ для студентов
Тут дофига бывает всего полезного. Печально, что бывают предметы по которым даже одного бесплатного решения нет, но это скорее вопрос к студентам. В остальном всё здорово.
Спасательный островок
Если уже не успеваешь разобраться или застрял на каком-то задание поможет тебе быстро и недорого решить твою проблему.
Всё и так отлично
Всё очень удобно. Особенно круто, что есть система бонусов и можно выводить остатки денег. Очень много качественных бесплатных файлов.
Отзыв о системе "Студизба"
Отличная платформа для распространения работ, востребованных студентами. Хорошо налаженная и качественная работа сайта, огромная база заданий и аудитория.
Отличный помощник
Отличный сайт с кучей полезных файлов, позволяющий найти много методичек / учебников / отзывов о вузах и преподователях.
Отлично помогает студентам в любой момент для решения трудных и незамедлительных задач
Хотелось бы больше конкретной информации о преподавателях. А так в принципе хороший сайт, всегда им пользуюсь и ни разу не было желания прекратить. Хороший сайт для помощи студентам, удобный и приятный интерфейс. Из недостатков можно выделить только отсутствия небольшого количества файлов.
Спасибо за шикарный сайт
Великолепный сайт на котором студент за не большие деньги может найти помощь с дз, проектами курсовыми, лабораторными, а также узнать отзывы на преподавателей и бесплатно скачать пособия.
Популярные преподаватели
Добавляйте материалы
и зарабатывайте!
Продажи идут автоматически
6486
Авторов
на СтудИзбе
303
Средний доход
с одного платного файла
Обучение Подробнее