Теперь вновь по Дракон-у. Прежде всего, необходимо определиться с пониманием "автоматности". Если же потребность в "автоматной наблюдательности", где "вводом/выводом" в каждом условно зафиксированном состоянии алгоритма занимается "наблюдатель" -- это одно дело. Однако, если те же базовые Р-схемы Вельбицкого есть лишь "пустые" (семантически) графические структуры, которые можно применять для формирования своих технологических языков моделирования, то в Дракон-е "уже всё украдено до нас" -- т.е., как и в блок-схемах, семантика задана, причём в жёсткой императивной форме. В отличие от графов переходов -- нет явных точек "наблюдения". Проблематика в данном случае решается методически -- условными инструкциями для читающего: мол при чтении наблюдатель останавливается на (или перед) иконой "действие", здесь можем узнать состояние (значения) переменных, на иконе "вопрос" -- не останавливаться, а "решаем" вопрос, и т.д. и т.п. Или же "выкраивать" автоматные состояния по Зюбину (рис. 2):
http://reflex-language.narod.ru/articles/03text_vs_graph.htmДругое дело -- алгоритм в виде автомата, "алгоритм-автомат" (так что ли назвать, не знаю как именно, чтобы сразу была конкретика уже в самом термине). Прежде всего, алгоритм-автомат должен отражать схему: "вход" -> "действие" -> "выход". Однако возникает потребность понимать "действие" уже не как элементарное и неделимое, хочется всё разложить по полочкам, с учётом фиксации предыстории вычислений.
Уточним особенности "канонической" модели "Дракон-автомата":
https://forum.drakon.su/viewtopic.php?f=142&t=4284#p78588По ссылке выше, согласно рисунку примера алгоритма разбиения текстовой строки на слова, получается примерно, в общих чертах, такая картина (псевдокод по Pascal-мотивам):
Код:
procedure Разбить_на_слова(текст, разделитель): список_строк;
var
i, символ, аккумулятор, список;
begin
аккумулятор := '';
список := [];
for i := 1 to len(текст) do
begin
символ := текст[i];
automaton
| Разделитель:
if символ = разделитель then
continue;
moveto Разделитель
else
аккумулятор := аккумулятор + символ;
moveto Слово;
end
| Слово:
continue;
if символ = разделитель then
добавить_аккумулятор_в_результат;
moveto Разделитель;
else
аккумулятор := аккумулятор + символ;
moveto Слово;
end
end
end;
добавить_аккумулятор_в_результат;
return список;
end;
(реализации подпрограммы "добавить_аккумулятор_в_результат" выше нет -- второстепенно). Оператор "automaton ... end" символизирует "автомат-силуэт" Дракон-а, оператор moveto -- переходы в ветки-состояния. Оператор continue -- некая вынужденная мера согласно примеру -- это особый оператор (в отличие от типового continue), необходимо понимать как начало новой итерации цикла, и как бы не покидая положения в алгоритме -- находясь в automaton при вызове continue выполняется временный выход из автомата, выполняется новая итерация цикла, и после отработки "символ := текст[i]" при входе в automaton вновь возвращаемся в исходное место (в результате вид веток-состояний чуть отличен от оригинала. И "подготовка" с "завершением" не заданы как ветки силуэта, чтобы выделить сам содержательный автомат как таковой, эти стадии задаются явно императивно согласно структуре алгоритма).
Таким образом в исходной схеме есть некая попытка скрыть внешний цикл для самого автомата (внешние "управляющие" циклы в каком-либо виде для автоматов неизбежны). Т.е. схема есть не строго по-Драконовски детальный императивный алгоритм ("без самодеятельности" и всё наглядно), а некий "протокол о намерениях" как какая-то спецификация:
https://forum.drakon.su/viewtopic.php?f=142&t=4284#p78600, для чего задаётся некая особая (частная!) семантика для икон "ввода" (всё-таки, метафора для икон ввода/вывода -- взаимодействие с внешней средой по отношению к самому алгоритму).
Отчасти таковы меры вынужденные, поскольку вложенных (в другие операторы/блоки) силуэтов нет (и трудно себе даже такую конструкцию представить, особенно с потребностью разбиения силуэта на листы).
Отмеченное выше положение, можно сказать, есть частный случай для типовых алгоритмов (для которых возникает естественное желание какого-то обобщения для удобства). Но в целом наблюдается некое семантическое перемешивание -- алгоритм выше является на самом деле то комбинаторным автоматом -- ему на вход дали текст и символ-разделитель, выполняется такт автомата (без никакого запомненного состояния по результатам проделанной работы в прошлом), и получен результат -- у алгоритма нет никаких иных вариантов для пользователей этого алгоритма. А все прыжки по goto в рамках силуэта выше -- лишь особенности реализации "действия" как условно линейного алгоритма. И силуэт в данном случае есть какой-то встроенный автомат внутри алгоритма (о чём ещё нужно догадаться), скорее, это некий "наблюдательский автомат" для изучения алгоритма, с основной особенностью -- возникает необходимость для читающего (и моделирующего) в локализации входа/выхода этого встроенного автомата (и каждого состояния) и их соотнесение с входом/выходом самого алгоритма. Т.е., согласно самой Дракон-схеме имеется глобальный (других-то и нет) силуэт как бы для самого алгоритма как такового, но на самом деле то это внутренний автомат в рамках спрятанного цикла. В случае, скажем, если "управленческий" цикл какой-то "не стандартный" (т.е. нечто иное от интуитивного перебора линейного списка/массива), то потребуется автомат явно куда-то вкладывать. Да и в любом случае скрытые, не декларируемые самой семантикой Дракон-а (подробной императивной), действия (и не простые то) не есть хорошо. Из-за отсутствия вложенных силуэтов остаётся возможность лишь применять отдельные схемы, напр.:
Код:
procedure анализ_символа(символ): строка;
var
аккумулятор = '';
automaton
| Разделитель:
if символ = '' then
yield аккумулятор;
аккумулятор := '';
moveto Разделитель;
else if символ = разделитель then
yield '';
moveto Разделитель
else
moveto Слово;
end
| Слово:
if (символ = разделитель) or (символ = '') then
yield аккумулятор;
аккумулятор := '';
moveto Разделитель;
else
аккумулятор := аккумулятор + символ;
yield '';
moveto Слово;
end
end;
procedure Разбить_на_слова(текст, разделитель): список_строк;
var
i, символ, слово, список;
begin
список := [];
for i := 1 to len(текст) do
begin
символ := текст[i];
слово := анализ_символа(символ);
if слово <> '' then
список.add(слово);
end
end;
слово := анализ_символа('');
if слово <> '' then
список.add(слово);
end;
return список;
end;
, где автомат выделен в отдельную процедуру "анализ_символа", причём само тело вместо "begin ... end" завёрнуто в "automaton ... end" для подчёркивания семантики, выражая тем самым глобальную конструкцию силуэта. Используется оператор yield -- возвращает результат как return, но лишь приостанавливает алгоритм. Это целенаправленный приём всего лишь для примера, вместо общения через аргументы и результаты процедур можно использовать операторы ввода/вывода (запустив на исполнение условно параллельно две процедуры, они будут общаться через какие-то каналы ввода/вывода, где "Разбить_на_слова" будет управляющим автоматом). В любом случае ключевое в автомате "анализ_символа" -- каждое состояние (ветка в силуэте) принимает вход и возвращает результат всего(!) алгоритма. Реагируя на каждый входящий символ, автомат "накапливает" вычисления и выдает непустую строку тогда, когда он разобрал слово. При пустом символе на входе выполняется выдача накопленных данных и производится "сброс" ("хозяин автомата" может решить прекратить работу ("уничтожить" автомат) или начать сначала, новую серию работ).
Алгоритм-автомат "анализ_символа" явно локализован, со своим входом/выходом и внутренним состоянием. Потребность в максимальной локализации сразу же ощущается, как только задействуется какая-то композиция автоматов (аналогичная проблематика при использовании процедур/функций с глобальными переменными, даже для отдельных "автоматных состояний" напрашивается использование параметров/аргументов вместо общих переменных), если что-то используется автоматами совместно, то это должны быть специальные "провода между агрегатами" -- специализированные средства/каналы общения.
Стиль построения кода выше -- по-Драконовски детально, т.е. все переходы и паузы (передача результата и возобновление работы) указаны явно, список состояний линеен. В других языках со специализированными автоматными конструкциями иногда поступают иначе. К примеру, объявляют лишь условия перехода (т.е. явно не указывая аля "moveto" на себя же), где-то понимание "пауз" может быть неявным исходя из семантики необходимых операторов, возможны общие условия переходов для состояний (для иерархических автоматов, групп, и для примера выше напрашиваются удобные средства для обработки общего условия проверки входного символа как пустого для организации "сброса" аккумулятора вместо дублирования функционала). Или же, напр., в состоянии "Слово" нет необходимости проверять условие перехода (первая ветка оператора if) при первом такте, когда оказываемся в этом состоянии (операторы перехода могут учитывать подобные факты). Т.е. семантика именно переходов несколько отлична от типового оператора if. В адекватных технологиях не допускают каскадных переходов -- не более одного на такт. И др.
И тогда возникает автоматная спецификация для алгоритма, аналогично спецификациям для объекта-файла из предыдущего сообщения -- есть возможность как для "серого" ящика уточнить семантику поведения алгоритма с учётом фиксации истории вычислений, с особенностями продвижения по этапам. Функциональное понимание алгоритма-автомата расширил Зюбин: полиморфная функция (функция с разными вариантами поведения/реализации) с динамической диспетчеризацией на основе событий (а не типов аргументов, к примеру. Или же в Лисп-ах есть некое близкое понятие "мульти-методов" -- диспетчеризация функций на произвольных гибких условиях. Проблематика в форме организации гибкой диспетчеризации, и задача непростая).
Замечания выше не стоит воспринимать, мол Дракон-автоматы должны быть именно какими-то такими. Выше всего лишь перечислены принципы, где основное: локализация "вход -> вариант алгоритма -> выход" плюс политика переключений вариантов. И если на любой автомат смотреть как на "наблюдательский автомат", то чем больше умолчаний или возникающего контекста на основе предыстории, тем сложнее или больше усилий для построения/понимания этой локализации. Если же силуэт автомат-алгоритма на самом деле какой-то встроенный с особенной политикой, то, видимо, должна быть какая-то стандартная семантика в Дракон-е, плюс понимание, когда автомат не встроенный, а "прямой" что ли.
И в Дракон-е ветки силуэта могут возникать не только для выражения алгоритмического "состояния" (принимающего вход и организующего результат в рамках всего алгоритма или какой-то его встроенной части, подразумеваемой как автомат, если такой принцип моделирования является неким стандартом). Или иными словами ветки-состояния могут распадаться на вспомогательные ветки-"не состояние" (по понятным причинам для удовлетворения потребностей выражения линейного алгоритма). В итоге не получится, скажем, взглянуть на всю совокупность заголовков веток и сразу же определить список алгоритмических "состояний", их выписать для интерфейса алгоритма и т.д. Если постановить, что любая ветка есть "состояние" вне зависимости как она возникает, то в любом случае возникает проблематика автоматной локализации, отмеченная выше.
Конструкция силуэта в Дракон-е сама по себе не выражает алгоритм-автомат как таковой явно, его ещё необходимо там выискивать (в общем случае имеется ввиду).
Как-то так, в общих чертах...