5Михаил Кузьмин писал(а):
PSV100 писал(а):
В неком файле System.rtf содержится какой-то концепт Automat. Может быть существует какая-то более "высокоуровневая" форма представления системы в виде взаимодействующих автоматов с изменяемым состоянием/поведением, комплексными "событиями" и пр. ?
Автомат это класс концептов с параметрами-объектами со стандартным взаимодейсвием между ними.
Попробую уточнить насчёт "более высокоуровневой" формы.
Примеры выше являются представителями "инверсии управления" -- если постановку задачи мы могли выразить условно в виде "неформальной системы уравнений" (ака конъюнкции выражений в системе уравнений, т.е. комплексно, сжато, в едином месте определив предписание), то сама реализация системы "разорвана на части" -- исходная постановка управления разнесена на куски в виде набора совместно исполняемых алгоритмов, взаимодействующих через общие объекты для реализации политики управления ("поставщики событий управляют процессом"). Без тестирования/интерпретации (и прочей верификации) нет уверенности в отсутствии косяков (и, в целом, понять общее управление на основе "исходного кода" не просто даже для такой малой задачки, поток управления теперь косвенный, его необходимо восстанавливать наблюдателю).
Попробуем уменьшить такой эффект. Как раз здесь на форуме в смежных темах вновь заговорили насчёт автоматных методик, заодно вспомнилось применение некоторых принципов "SWITCH-технологии":
https://forum.drakon.su/viewtopic.php?f=62&t=6097&start=40#p100667https://forum.drakon.su/viewtopic.php?f=62&t=6097&start=40#p100673https://www.kit-e.ru/articles/circuit/2006_12_118.phpСистема представляется как комплекс взаимодействующих автоматов. Вводится некое понятие такта системы (аля шаг-итерация условного "управляющего" цикла). Автоматы "выявляют" события и уведомляют своих "соседей", но с целью упрощения разруливания взаимосвязей осуществляется задержка "обнаружения событий" на один такт.
Потренируемся на примерах выше, схематично:
Код:
type
// Тип-enum, выражающий текущее состояние "события":
// - неактивное (необнаруженное);
// - установленное (но доступное "потребителям" на следующем такте);
// - активное (доступное "потребителям")
EventState = {Inactive, Stated, Active};
// enum как "автоматное" состояние процесса печати символов
PrintState = {Active, Idle};
var
// состояние "события" нажатия управл. клавиши
EventCmd: EventState := Inactive;
// состояние "события" нажатия симв. клавиш
EventChar: EventState := Inactive;
// код веденного символа
Char: int = 0;
// флаг для фиксации события "предыдущего" нажатия управл. клавиши
PreCmd: bool := false;
// текущее "автоматное" состояние процесса печати символов
ProcPrintState: PrintState := Active;
// процесс обработки состояний "событий" в конце каждой итерации системы
// для подготовки следующего такта:
// Если событие было "активным", то оно "переводится" в "неактивное",
// если было "обнаруженным" -- в "активное"
// (также фиксируется "предыдущее нажатие" управл. клавиши)
function ProcessEvents() {
if EventCmd = Active then
EventCmd := Inactive;
PreCmd := true;
elif EventCmd = Stated then
EventCmd := Active;
else
PreCmd := false;
end;
if EventChar = Active then
EventChar := Inactive;
elif EventChar = Stated then
EventChar := Active;
end;
};
// функция для фиксации "обнаружения" ("установки") события нажатия управл. клавиши
function SetCmd() {
EventCmd := Stated;
}
//...символьной. клавиши
function SetChar(c: int) {
EventChar := Stated;
Char := c;
}
// функция для опроса "получения" (или "существования установки") события нажатия упр. клавиши
function RecvCmd(): bool {
return EventCmd = Active;
}
//...символьной. клавиши
function RecvChar(): bool {
return EventChar = Active;
}
// Процесс обнаружения нажатия управл. клавиши
function ProcessCmd() {
...
if cond then // каким-то образом обнаруживаем "внешний ввод нажатия"
SetCmd();
end;
};
//...символьной. клавиши
function ProcessChar() {
...
if cond then
SetChar(GetChar());
end;
};
// Процесс печати символов в виде "автомата" из двух состояний.
// Основная логика управления ключевым "прикладным" процессом
// теперь собрана в одном месте (в разрезе конкретных автоматных состояний,
// и "переходом" в эти состояния теперь занимается лишь сам данный процесс),
// в т.ч. с учётом того, что во время такта системы могут
// отсутствовать "целевые" события.
function ProcessPrint() {
case ProcPrintState of
| Active:
if RecvCmd() & PreCmd then
ProcPrintState := Idle;
elif RecvChar() & PreCmd then
print(toUpper(Char));
elif RecvChar() then
print(Char);
end;
| Idle:
if RecvCmd() then
ProcPrintState := Active;
end;
end;
};
// главная императивная часть в виде некоего "управляющего" цикла
// (каждая итерация есть "такт" системы)
begin
loop
ProcessCmd();
ProcessChar();
ProcessPrint();
ProcessEvents();
end;
end;
Большая часть содержания выше это, скорее, реализация "фреймворка" для обеспечения работы процесса печати (однако нужная, чтобы выразить суть принципов снижения "инверсии управления"). Недостаток методики -- в задержке реакций на один такт, при этом сложновато выразить/выявить "предшествующие" события аля "нажатие упр. клавиши на пред. такте" (да и технически "дёргаются" все процессы при каждой итерации).
К примеру, средства, реализующие так называемую
"синхронную модель" вычислений (также на основе политики "логических тактов") могут быть более гибкими/оптимальными -- уже конструктивно выявлять все зависимости по производству/потреблению данных и организовывать соответствующее следование реакций, или же в runtime блокировать/приостанавливать потребителей до возникновения события или конца такта системы (или не активировать процессы без надобности) и пр.
На каком-нибудь Lustre/Lucid Synchrone процесс печати выше, например, может быть сведён к системе уравнений, нечто вроде (диалект ML, упрощённо по мотивам -- определен процесс, принимающий на вход нажатия упр. и симв. клавиш, стандартный оператор "pre(...)" означает "пред. значение" (вместо "ручного" сохранения результатов "высказываний о состоянии"), имеются и иные "потоковые" операторы):
Код:
let ProcessPrint(cmd: bool, char: int) = void where
automaton
| Active =>
do
if char & pre(cmd) then print(toUpper(char))
elif char then print(char)
unless cmd & pre(cmd) then Idle
| Idle =>
do () unless cmd then Active
end