Опубликован: 11.02.2005 | Уровень: специалист | Доступ: свободно
Лекция 9:

Автоматное программирование: анализ задачи

< Лекция 8 || Лекция 9: 12345 || Лекция 10 >

Табличное представление графа состояний

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

Различают визуальные представления таблиц переходов, которые предназначены для человека, и их программные представления. Требования к визуальным представлениям - понятность для человека, статическая проверяемость свойств; требования к программным представлениям - выполнимость при посредстве какой-либо системы программирования; общее требование к представлениям обоих видов - однозначное соответствие между ними, позволяющее обоснованно утверждать вычислительную эквивалентность.

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

  • трансляционная - генерируется программа, структура управления которой определяется графом состояний ;
  • интерпретационная - строится специальная программа, которая воспринимает некое представление графа как задание для интерпретации.

Как при трансляционной, так и интерпретационной позиции возможен целый спектр реализаций. Ближайшая же цель в том, чтобы научиться удобно для человека представлять граф состояний и переходов. Наиболее естественно описывать его в виде таблицы. Для методов на переходах и в состояниях таблицы несколько различаются. На табл. 9.1 представлен случай Мили. Опишем значение колонок таблицы.

  1. Наименование состояния - входы в таблицу.
  2. Условие (срабатывания) перехода - логическое выражение или служебное слово failure, которое указывает на действие, выполняемое, если ни одно из условий не срабатывает.
  3. Действие, ассоциированное с переходом, - последовательность операторов, выполняемая, когда условие перехода истинно.
  4. Адрес перехода - наименование состояния -преемника.

Кроме того, определяется специальная (первая) строка, в которой помещаются операторы, инициирующие процесс, и адрес перехода начального состояния.

В табличном представлении графа переходов в состояниях (таблица 9.2) действия помещаются во втором столбце, сразу после обозначения состояния, а уже затем идут переходы. Анализируя таблицу 9.2, можно заметить ряд повторяющихся действий. Конечно же, если бы эти действия были более сложными, их просто описали бы как процедуры. Но такие повторения заставляют задуматься, а нельзя ли сократить число состояний? Введя дополнительную булевскую переменную, отметившую, что последним символом был перевод строки, мы могли бы избавиться от пары состояний. Но такой переменной нужно присваивать новое значение в процессе проверки условий, и при этом в строго определенный момент: сначала проверить, не является ли символ концом строки, а уже затем переприсваивать данную переменную. Это решение сильно отдает хакерством, и, если Вы хотите писать качественные, легко перестраиваемые программы, то таких решений нужно избегать.

Еще раз показать тонкую грань между хакерством и естественностью можно, рассмотрев один из моментов, возникающих в наших программах. При работе в состояниях нам приходится обнулять счетчик букв слова в тот момент, когда кончается очередное слово, поскольку иначе пришлось бы выделять еще одно состояние, соответствующее середине обработки слова. Но это гораздо менее искусственное решение: неплохо восстанавливать начальные значения и делать другие действия по приведению в порядок информации в тот момент, когда заканчивается очередной этап работы; неплохо делать это всегда в начале нового этапа; плохо лишь произвольно перемешивать эти две дисциплины.

Таблица 9.1. Табличное представление графа для действий на переходах

char symbol; // Чтение потока символов неявное

// Чтение происходит перед выполнением проверок и действий

int Cnt; . . . // Инициализация

St1
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
St1 /* Действий нет */ 'a'<=symbol && symbol <= 'z' St2
/*(symbol<'a'|| 'z'<symbol) &&*/ symbol!='\n' St1
symbol='\n' St3
St2 printf ("%c", symbol); Cnt++; 'a'<=symbol && symbol <= 'z' St2
/*(symbol<'a'|| 'z'<symbol) &&*/ symbol != '\n' St4
symbol='\n' St5
St3 /* Действий нет */ 'a'<=symbol && symbol <= 'z' St2
/*(symbol<'a'|| 'z'<Symbol) &&*/ symbol!='\n' St1
Второй '\n' дальше не нужно читать symbol='\n' exit
St4 printf ("- %i\n", Cnt); Cnt=0; 'a'<=symbol && symbol <= 'z' St2
/*(symbol<'a'|| 'z'<symbol) &&*/ symbol != '\n' St1
symbol='\n' St3
St5 printf ("- %i\n", Cnt); Cnt=0; 'a'<=symbol && symbol <= 'z' St2
/*(symbol<'a'|| 'z'<symbol) &&*/ symbol != '\n' exit
Второй '\n' дальше не нужно читать symbol='\n' exit
exit /* Нет неявного чтения потока */ return 0; // Считать данную секцию таблицы состоянием или нет - дело вкуса
< Лекция 8 || Лекция 9: 12345 || Лекция 10 >
Федор Антонов
Федор Антонов

Здравствуйте!

Записался на ваш курс, но не понимаю как произвести оплату.

Надо ли писать заявление и, если да, то куда отправлять?

как я получу диплом о профессиональной переподготовке?

Илья Ардов
Илья Ардов

Добрый день!

Я записан на программу. Куда высылать договор и диплом?