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