Добавил:
ivanov666
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз:
Предмет:
Файл:Стили и методы программирования. Учебное пособие для СПО
.pdf
Методы действий в состояниях и на переходах: анализ
состояний и построение таблицы
В данном разделе начинается детальный показ двух методов
автоматного программирования, основанных на модели Мура и Мили.
Методика работы в них почти одна и та же, но, как чаще всего бывает,
маленькое и вроде бы техническое различие (чему сопоставляются
действия: состояниям или переходам?) порождает два несовместимых
метода. Их несовместимость не столь грубая, как во многих других
случаях (два таких случая - несовместимость между автоматами и
присваиваниями и между циклами и рекурсиями - рассмотрены ниже).
Если это и противоречие, то противоречие технологическое.
Произвольно перемешивая эти два метода, мы в дальнейшем затрудняем
модификацию программы, а в настоящем вынуждены множить число
подпорок.
Человек даже грубые противоречия игнорирует или обходит
4)
. Для
применения правила важно знать не только его, но и когда можно его
нарушать. Общеметодологический принцип здесь такой: если уж
нарушать, то на полную катушку (проехав перекресток на красный свет,
глупо останавливаться сразу за ним)! Второй принцип: если нельзя, но
очень нужно, то надо! А. А. Шалыто указал, что в теории
5)
есть и такое
смешанное понятие, как автоматы Мили-Мура. Одной из моделей таких
автоматов является автомат с двумя выходными лентами: на одну
пишется результат в состояниях, на другую - на переходах. Программной
интерпретацией такой теоретической модели является, например,
модуль, который в состояниях ведет диалог с окружающей средой, в
результате чего получает данные для определения очередного перехода,
а на переходах производит внутренние вычисления.
Для отработки методики используется простая задача, не затемняющая
ее суть частными тонкостями и пригодная для реализации любым из
двух методов. Полученная методика переносится на широкий класс
задач и не отказывает вплоть до тысяч состояний.
Постановка задачи и первичный анализ
Пусть требуется решить следующую задачу. Словом называется любая
Стили и методы программированияНепейвода Н.Н.
151

непустая последовательность букв латинского алфавита (для простоты только строчных букв). Перепечатать из входной последовательности
символов все максимальные входящие в нее слова в следующем виде:
<слово> - <длина слова><конец строки печати>
Ввод заканчивается пустой строкой.
Например, по строкам
попугай бегемот
1мот2крот1мот
нужно выдать что-либо вроде
попугай 7
бегемот 7
мот 3
крот 4
мот 3
Для решения задачи входную последовательность символов естественно
считать потоком, читаемым слева направо, пока не будет прочитана
пустая строка. При чтении букв, других символов и конца строки
действия, которые необходимо выполнять, различны. Кроме того,
различаются действия для первой буквы и для последующих букв слова.
Построение графа состояний
Прежде всего, определим набор состояний, исходя из различных
реакций на прочитанный символ.
Вот полный перечень вариантов реакций:
1. Символ буква: инициализировать обработку слова (счетчику
длины слова присвоить единицу).
2. Символ буква: продолжить обработку слова (счетчик длины слова
увеличить на единицу).
3. Символ не буква: закончить обработку слова (напечатать длину
Стили и методы программированияНепейвода Н.Н.
152

прочитанного слова).
4. Символ не буква: пропустить символ.
5. Символ конец строки: закончить обработку слова (напечатать
длину прочитанного слова).
6. Символ конец строки: пропустить символ.
7. Символ конец строки: завершить процесс.
Есть еще одно действие, не отраженное в этом списке, но
подразумеваемое: стандартное действие, происходящее перед началом
отработки нового состояния. В данном случае это чтение очередного
символа, которое должно выполняться, когда переход срабатывает. В
других случаях автоматных моделей роль чтения очередного символа
может играть, скажем, последующий шаг моделирования. Для данного и
для большинства других автоматных примеров стандартное действие не
нужно указывать явно, поскольку оно автоматически сопоставляется
каждому переходу и полностью соответствует математической модели.
Из перечня действий для рассматриваемой задачи следует, что должно
быть, как минимум, два класса состояний, имеющие разные действияреакции.
Следующий методический прием - это определение начального
состояния, т. е. того, в котором вычислительный процесс
активизируется. Нужно указать, какие действия возможны в данном
состоянии и какие условия перехода в нем должны быть.
Для рассматриваемого случая в начальном состоянии ( St1 ), возможны
переходы:
1. с переходом в другое состояние - St2 (поскольку следующая буква
требует другой реакции),
1. с сохранением состояния и
1. отработка первого перевода строки.
Стили и методы программированияНепейвода Н.Н.
153

Рис. 9.4. Начало построения графа состояний по Муру
Результат только что проведенного анализа можно представить в виде
графа, изображенного на рис. 9.4
. С каждой дугой графа связано условие
перехода. С каждым состоянием может быть связано действие. В
состояниях St1 и St3 действием является пропуск очередного символа и
чтение следующего, но соединить их сейчас нельзя, поскольку в первом
из них переход на завершение работы невозможен, так что перевод
строки анализируется по-разному. Это позволяет предположить, что в
данной задаче действия стоит ассоциировать с переходами, а не с
состояниями. Правильный выбор того, с чем ассоциировать действия,
может сильно оптимизировать программу. Для показа двух возможных
вариантов, которые в данном случае почти эквивалентны по
сложности, мы будем действовать как на переходах, так и в состояниях.
В частности, предварительный анализ состояний при методе
преобразований на переходах дан на рис. 9.5
.
Стили и методы программированияНепейвода Н.Н.
154

Рис. 9.5. Начало построения графа состояний при использовании
метода на переходах
Внимание!
Если у Вас есть две близкие, но концептуально противоречивые,
модели, в начале развития программы нужно сделать выбор в пользу
одной из них. Небольшие эксперименты и отступления в начале
оборачиваются большой экономией сил в конце.
На схемах вход и выход обозначаются черными прямоугольными
блоками. При желании их можно считать специальными состояниями,
с которыми ассоциированы действия инициализации и завершения
процесса. Состояние St2 изображено штриховой линией, поскольку
анализ его еще не проведен. Для каждого из еще не
проанализированных состояний (в данном случае для St2 ) следует
определить условия перехода (и соответствующие им действия, если
выбрали работу на переходах ).
В рассматриваем случае в состоянии St2 возможны переходы:
Стили и методы программированияНепейвода Н.Н.
155

1. с сохранением состояния ;
1. окончание слова и отработка вывода слова, поскольку следующий
символ не буква, с переходом в другое состояние, которому дается
временное имя St4 ;
1. отработка окончания слова и перевода строки, ему можно дать
временное имя St5.
После этого построения проверяется, не является ли новое состояние
копией уже имеющихся как по действиям, так и по переходам. Если это
так, то новое состояние можно отождествить (склеить) с одним из ранее
построенных. Вновь появляющиеся состояния анализируются
аналогично.
Для решаемой задачи легко выяснить, что состояние St3 требует тех же
действий и переходов, что и St5, а St4 изоморфно St1. Следовательно,
возможна склейка этих двух состояний со старыми и построение графа
завершается, так как новых состояний нет (см. рис. 9.6
). С тем, чтобы не
дублировать действия <Завершить процесс>, можно определить еще
одно, заключительное состояние, с выходом из которого будет
ассоциировано это действие (один раз!).
Рис. 9.6. Полученный граф состояний при действиях на переходах
Стили и методы программированияНепейвода Н.Н.
156

Аналогично можно поступить и когда мы работаем в состояниях. Здесь
процесс удлиняется на один шаг, поскольку необходимо выделить
завершение обработки слова в отдельное действие (причем в двух
вариантах: после перевода строки и после других небуквенных
символов). Полученный результат показан на рис. 9.7
.
Рис. 9.7. Полученный граф состояний при действиях в состояниях
Табличное представление графа состояний
Графическое или иное представление графа состояний конечного
автомата, явно отражающее наименования состояний, условия
переходов между состояниями и ассоциированные с ними действия,
называют таблицей переходов . Такое представление является одним из
центральных компонентов широко используемого в индустриальном
программировании языка объектно-ориентированного дизайна UML (в
частности, в форме, реализованной в системе Rational Rose ) - state
transition diagrams (диаграммы состояний и переходов ).
Стили и методы программированияНепейвода Н.Н.
157

Различают визуальные представления таблиц переходов, которые
предназначены для человека, и их программные представления.
Требования к визуальным представлениям - понятность для человека,
статическая проверяемость свойств; требования к программным
представлениям - выполнимость при посредстве какой-либо системы
программирования; общее требование к представлениям обоих видов однозначное соответствие между ними, позволяющее обоснованно
утверждать вычислительную эквивалентность.
Ответ на вопрос, какое представление графа состояний лучше всего
использовать, зависит от сложности графа, статичности либо
динамичности его и того, для каких целей требуется спецификация в
виде графа состояний. Понятно, что важнейшей для нас промежуточной
целью является программа на алгоритмическом языке. Но подходы к
построению такой программы могут быть различны. Существует две
принципиально различные методики применения спецификаций:
трансляционная - генерируется программа, структура управления
которой определяется графом состояний ;
интерпретационная - строится специальная программа, которая
воспринимает некое представление графа как задание для
интерпретации.
Как при трансляционной, так и интерпретационной позиции возможен
целый спектр реализаций. Ближайшая же цель в том, чтобы научиться
удобно для человека представлять граф состояний и переходов.
Наиболее естественно описывать его в виде таблицы. Для методов на
переходах и в состояниях таблицы несколько различаются. На табл. 9.1
представлен случай Мили. Опишем значение колонок таблицы.
1. Наименование состояния - входы в таблицу.
2. Условие (срабатывания) перехода - логическое выражение или
служебное слово failure, которое указывает на действие,
выполняемое, если ни одно из условий не срабатывает.
3. Действие, ассоциированное с переходом, - последовательность
операторов, выполняемая, когда условие перехода истинно.
4. Адрес перехода - наименование состояния -преемника.
Кроме того, определяется специальная (первая) строка, в которой
Стили и методы программированияНепейвода Н.Н.
158

помещаются операторы, инициирующие процесс, и адрес перехода
начального состояния.
В табличном представлении графа переходов в состояниях (таблица 9.2)
действия помещаются во втором столбце, сразу после обозначения
состояния, а уже затем идут переходы. Анализируя таблицу 9.2, можно
заметить ряд повторяющихся действий. Конечно же, если бы эти
действия были более сложными, их просто описали бы как процедуры.
Но такие повторения заставляют задуматься, а нельзя ли сократить
число состояний? Введя дополнительную булевскую переменную,
отметившую, что последним символом был перевод строки, мы могли
бы избавиться от пары состояний. Но такой переменной нужно
присваивать новое значение в процессе проверки условий, и при этом
в строго определенный момент: сначала проверить, не является ли
символ концом строки, а уже затем переприсваивать данную
переменную. Это решение сильно отдает хакерством, и, если Вы хотите
писать качественные, легко перестраиваемые программы, то таких
решений нужно избегать.
Еще раз показать тонкую грань между хакерством и естественностью
можно, рассмотрев один из моментов, возникающих в наших
программах. При работе в состояниях нам приходится обнулять счетчик
букв слова в тот момент, когда кончается очередное слово, поскольку
иначе пришлось бы выделять еще одно состояние, соответствующее
середине обработки слова. Но это гораздо менее искусственное
решение: неплохо восстанавливать начальные значения и делать другие
действия по приведению в порядок информации в тот момент, когда
заканчивается очередной этап работы; неплохо делать это всегда в
начале нового этапа; плохо лишь произвольно перемешивать эти две
дисциплины.
Таблица 9.1. Табличное представление графа для действий на переходах
char symbol; // Чтение потока символов
неявное
// Чтение происходит перед выполнением
проверок и действий
int Cnt; . . . // Инициализация
St1
Стили и методы программированияНепейвода Н.Н.
159

St1
'a'<=symbol &&
symbol <= 'z'
printf ("%c", symbol); Cnt
= 1;
St2
/*(symbol<'a'||
'z'<symbol) &&*/
symbol!='\n'
/* Действий нет */ St1
symbol='\n' /* Действий нет */ St3
St2
'a'<=symbol &&
symbol <= 'z'
printf ("%c",
symbol);cnt++;
St2
/*(symbol<'a'||
'z'<symbol) &&*/
symbol != '\n'
printf ("- %i\n", Cnt);
St1
symbol='\n'
printf ("- %i\n", Cnt);
St3
St3
'a'<=symbol &&
symbol <= 'z'
printf ("%c", symbol); Cnt
= 1;
St2
/*(symbol<'a'||
'z'<Symbol) &&*/
symbol!='\n'
/* Действий нет */ St1
symbol='\n'
Второй '\n' дальше не
нужно читать
exit
exit
/* Нет неявного
чтения потока */
return 0; // Считать
данную секцию таблицы
состоянием или нет - дело
вкуса
Таблица 9.2. Табличное представление графа для действий в состояниях
char symbol; // Чтение потока символов
неявное
// Чтение происходит после выполнения
действия, перед проверками
int Cnt; ...Cnt=0;
// Инициализация
St1
/* Действий
нет */
'a'<=symbol && symbol <= 'z' St2
Стили и методы программированияНепейвода Н.Н.
160
Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]
