Всем привет, с вами вновь сумасшедший профессор (хотя совсем не профессор и возможно не совсем сумасшедший). Разберем очередную актуальную тему или не очень актуальную.

Как конечные автоматы заставляют нас бросить решение реальной задачи и уводят в сферические псевдо-математические дебри.

Или можно ли для произвольной задачи программирования найти практический смысл?


Вот тут вот мне попался интересный пример попытки объяснения конечных автоматов как математической модели вычислений (офигеть!):

Понятие о конечных автоматах: руководство разработчика по предсказуемой логике приложений

По моему идеальный пример чтобы разобрать как конечные автоматы (как пример паттерна проектирования) уводят нас в сторону от решения реальных задач программирования и проектирования программируемых функций электронных устройств.

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

Что же такое светофор с инженерной точки зрения?

Это светофор?
Это светофор?

На самом деле это три лампочки и три ключа для управления — включения-выключения каждой из этих лампочек. Наша вычислительная система (просто процессор) должна управлять состоянием ключей, а ключей у нас три и состояний получается тоже вроде бы три, правда состояния отдельных лампочек вроде бы проще, только ВКЛ и ВЫКЛ. Но это только для домохозяек. Те кто маленько разбираются в электронике (а не только в языках… программирования) знают что состояний вообще говоря больше, так как вы можете включить лампочку, а она не включится! Как это учесть? Это вообще можно учесть? Получается у композиции, даже такой примитивной как лампочка-ключ (выключатель), не два состояния, а вообще говоря четыре:

  1. включили лампочку-она горит

  2. включили лампочку-она НЕ горит

  3. вЫключили лампочку-она не горит

  4. вЫключили лампочку-она (все равно!) горит

Как же это вообще программировать? Оказывается это надо не программировать, а понимать как это реализуется схемотехнически! У нас для каждой пары лампочка-ключ должен быть одна управляющая линия (контакт, нога, пин, GPIO, …) И

одна контролирующая-входная-сигнальная линия (контакт, нога, пин, GPIO, …)!

Итого у нас оказывается два бита состояния — один бит задаем мы, второй возвращает нам лампочка. Два бита — это четыре состояния, все сходится.

Вот так вот мы выяснили что у одной управляемой лампочки может быть больше состояний чем у целого светофора! Маленько попозже мы вернемся к этим четырем состояниям для одной лампочки, а пока вернемся к нашему целому светофору.

Вообще-то, когда у светофора зеленый свет вскоре должен переключиться он начинает моргать, скажем три раза. Он должен погаснуть и загореться. У нас была такая красивая стейт машина из трех состояний и куда, а главное как нам отразить это новое состояние моргающий зеленый? Чем нам помогла наша исходная стейт-машина, я что-то, хоть убейте, не вижу! Но есть и другие вопросы, например: «А это вообще состояние?» Давайте попробуем опять разобраться, с инженерной точки зрения, что такое моргающий зеленый? Это значит что зеленая лампочка то горит то гаснет, с инженерной точки зрения это определяется временем периодов, когда она горит и потом НЕ горит. Оказывается что состояние светофора определяется в том числе временем-периодами. Если лампочка определенного цвета горит достаточно долго мы определяем это как состояние соответствующего цвета, это просто красный, желтый, зеленый, в случае светофора.

Если лампочка то включается, то гаснет, у нас на бытовом уровне, есть такие понятия как моргающий зеленый и моргающий желтый, как минимум. Это более сложные состояния и это отражается в названии этих состояний.

Но это все состояния, которыми мы оперируем как потребители функции светофора, и к которым мы привязываемся в бытовых ситуациях связанных с сигналами светофора. Это совершенно не то что нам нужно для понимания или проектирования светофора как технического изделия которое должно обеспечить определенные технические параметры.

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

красный : 20 секунд

желтый : 5 секунд

зеленый : 17 секунд

ВСЕ выключено : 1 секунда

зеленый : 1 секунда

ВСЕ выключено : 1 секунда

зеленый : 1 секунда

ВСЕ выключено : 1 секунда

зеленый : 1 секунда

КОНЕЦ таблицы!

Код который работает по этой таблице будет примерно такой:

tablRec =  Table.firstRec();
while(true)
{
	setLampsState(tablRec.colors);
	timer  = setTimer(tablRec.period);
	waitFor(timer);
	 tablRec = nextRec(Table);
	if(tablRec == 0)
		 tablRec =  Table.firstRec();
}

Представляете! И это все! Сравните с любой стейт-машиной, которую можно было бы разрабатывать (долго и упорно) вместо этих 10 строчек и одной линейной таблицы, вся логика записана последовательно, все перед глазами! Это называется хорошо локализованная логика.

Как видите стейт-машина нам оказалась не просто не нужна, без нее все выглядит намного проще, а самое главное совершенно понятно как этим управлять! Запишите другие периоды в таблицу если вам надо управлять другим типом трафика! Гораздо проще просто записать состояния в таблицу и просто загружать это состояние по соответствующему событию, а самое главное это абсолютно прозрачно управляется конфигурируется. И если попробовать проанализировать, что же у нас является состоянием в этом коде, то можно придти к мнению что текущим состоянием, которое подлежит загрузке в систему является не только состояние лампочек светофора, состояние включает в себя еще и периоды на которые эти лампочки зажигаются или выключаются! Оказывается состояние системы, даже такой простой как светофор на инженерном языке выражается немного сложнее чем мы привыкли в повседневной жизни. Но при этом очень просто и детерминировано-прозрачно эти состояния можно записать в виде программы и таблицы данных для этой программы. Оказывается, если правильно разделить описание системы на список данных по состояниям этой системы и процедуру, которая позволяет переключать эти состояния по событиям, которые происходят в системе то программа получается намного проще.

Теперь можно вспомнить что у лампочки светофора на самом деле может быть четыре состояния, и что мы можем включить лампочку, а она не загорится. Можно ли учесть это в этом псевдокоде нашей программы? Очень просто! Достаточно заменить простой вызов:

setLampsState(tablRec.colors);

вызовом с условием, например так:

inColors = setLampsState(tablRec.colors) ;
if (inColors !=  tablRec.colors)
{
	setError (parseColors(tablRec.colors, inColors ));
	break;
}

И нам придется выйти из цикла и куда же мы попадем?

В самом простом случае нам придется погасить все лампы светофора, но есть еще вариант включить резервное аналоговое управление которое, наверно, должно включать и выключать желтый сигнал светофора.

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

Всем удачи и понимания того что вы на самом деле делаете.

Комментарии (19)


  1. Forthright
    06.06.2026 06:17

    Статья напоминает типичную аргументацию противников ООП. В духе "Вот смотрите, давайте предположим что нам нужно сложить два числа. Без ООП мы напишем а+б и получим ответ, но с ООП мы создадим класс числа, инкапсулируем значение, перегрузим оператор сложения и только после этого сложим числа. И кому нужны такие сложности?". И типо естественно на искусственном примере, который мало того что элементарный, но и буквально подогнан специально для доказательства конкретной точки зрения, будет работать выбранная концепция, какой бы она ни была. Аргументации натащить не проблема. Вот только в реальном программировании у вас не будет три/пять состояний с одним переходом на каждое. У вас будет система с пятнадцатью состояниями, реализующими свои сегменты алгоритма со сложной логикой переходов. И там уже любые способы упростить количество внешнего влияния на локальный сегмент алгоритма будут снижать сложность на порядки, будь то инкапсуляция или использование машины состояний. Но нет, зачем учить пропись, если "мама" можно печатными буквами написать и водители фур лохи, зачем так далеко ездят, если за любыми покупками можно пешком в пункт выдачи маркетплейса под домом сходить.


    1. rukhi7 Автор
      06.06.2026 06:17

      все бы было хорошо с вашей претензией, если бы это был действительно совсем искусственный пример. На самом деле это настоящее решение реальной задачи в обобщенном виде (в псевдо-коде). Задача действительно выбрана простая, но как минимум для класса простых задач это решение замечательно работает. Это очевидно, может раздражает как раз то насколько это очевидно? Для сложных задач можно применить декомпозицию и может оказаться что сложные задачи тоже состоят из простых подзадач.


      1. MaxAhmed
        06.06.2026 06:17

        Я может быть не очень внимательно читал. Но исходный посыл что про не нужность и искусственность стейт машин из статьи как-то не следует. Вот это

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

        По сути описание конечного автомата - то есть системы с конечным числом состояний и переходов. А уж как его реализовывать в каждом конкретном случае это к самой концепции конечного автомата отношение не имеет.

        И да прежде чем начинать разработку желательно проанализовать задачу. Возможно имеет смысл сделать автомат для лампочки например с состояниями горит, не горит, мигает и сломалась и отдельный для светофора с состоянми: красный,переключение на зеленый, зеленый ,переключение на красный и мигающий желтый (не исправность. )


        1. rukhi7 Автор
          06.06.2026 06:17

          тут есть один хитрый аспект, мне кажется, который я (да!) не смог сформулировать-раскрыть во всей его неоднозначности. Если состояние задается, как для этого случая, например, тремя битами для выключателей плюс скажем 16-ть битов (short) значение задержки, итого получается два в 19-й степени возможных состояний. Это все еще конечное число состояний? То есть имеет ли смысл рассматривать такое большое число состояний как конечное?

          Есть предел когда конечный автомат перестает быть конечным? И если этот предел существует, что там за пределом? По моему пустота не познанного пока.

          Или можно с другой стороны зайти. Три бита для выключателей действительно задают конечное число состояний, как не крути, но когда они идут в комплекте со значением , которое по своему характеру не дискретное (хоть и приводится к определенному разрешению и значит дискретности) этот комплект это все еще дискретное состояние или уже не дискретное?


          1. Imaginarium
            06.06.2026 06:17

            А приведите, пожалуйста, пару задач, в которых FSM действительно нужна.


            1. rukhi7 Автор
              06.06.2026 06:17

              На фундаментальном уровне процессор (CPU) — это сложный конечный автомат (Finite State Machine, FSM), который управляет потоком данных и выполняет инструкции, переключаясь между предопределенными состояниями.

              Изначально конечные автоматы появились именно применительно к CPU которые были достаточно примитивными чтобы оставаться в рамках этой теории конечных автоматов. Но современные процессоры настолько сложны и сочетают в себе столько разных технологий, что применительно к ним про FSM уже давно никто не вспоминает. Даже одно понятие конвейер уже не укладывается в рамки теории конечных автоматов.


              1. Imaginarium
                06.06.2026 06:17

                Даже одно понятие конвейер уже не укладывается в рамки теории конечных автоматов.

                – докажите. Очень, очень сильное утверждение.

                То есть, FSM нигде не нужны толком, я правильно понимаю?


                1. rukhi7 Автор
                  06.06.2026 06:17

                  сколько состояний есть у конвейера который способен обрабатывать примерно 100 (сто) типов команд (оценка минимального размера списка инструкций ассемблера)? и где половина и этих команд имеет параметры из достаточно больших непрерывных диапазонов. При чем эти команды конвейером разделяются на скажем 4 фазы и накладываются, соответственно со сдвигами,то есть все это богатство фактически возводится в какую-то степень.

                  Вы считаете есть какой-то смысл рисовать-строить-анализировать это как стейт-машину с миллиардами состояний?


          1. tmblwd
            06.06.2026 06:17

             Если состояние задается, как для этого случая, например, тремя битами для выключателей плюс скажем 16-ть битов (short) значение задержки, итого получается два в 19-й степени возможных состояний. Это все еще конечное число состояний? То есть имеет ли смысл рассматривать такое большое число состояний как конечное?

            Вы либо никогда в реальной жизни не использовали FSM, либо просто прикалываетесь.

            Почти во всех реализациях FSM можно передавать payload вместе с переходом, если уж так сильно надо, и это никак не связано с количеством возможных состояний.

            Если задержка не влияет на поведение переходов, то и передавать не обязательно, и она будет учитываться где-то в другом месте, там где реализована ф-я мигания. Вы же сами решаете, что именно моделирует ваш автомат


  1. BobovorTheCommentBeast
    06.06.2026 06:17

    Помоему вы сейчас стейт паттерн переизобретете


    1. rukhi7 Автор
      06.06.2026 06:17

      это запрещено?

      Вообще то это я не сейчас изобретаю, я давно этим пользуюсь, просто решил опубликовать как интересную-полезную технику реализации определенного класса задач. Спасибо всем кто помогает мне это формулировать!


  1. OlegPowerC
    06.06.2026 06:17

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

    Как пример - TCP.

    А еще можете ознакомится с такой штукой:

    https://www.state-machine.com

    Однако это не догма, если задача решается как у вас то почему нет?


  1. Katasonov
    06.06.2026 06:17

    Если кратко сформулировать содержание то получим "те же яйца, только с боку".


  1. Elbrus128
    06.06.2026 06:17

    Очевидно, автор легко продолжит серию публикаций статьёй "Почему нахрен не нужна нормализация таблиц БД и язык SQL", рассказав, что если есть данные: имя, должность, кабинет, телефон, эл. почта для 3 сотрудников, то это можно записать в переменные и быстро—быстро извлекать эти значения из ОЗУ.

    Ждём.


    1. Z55
      06.06.2026 06:17

      Примерно так и появился Redis ))


  1. mih-kopylov
    06.06.2026 06:17

    Статья выглядит как пример в защиту вполне очевидного тезиса “Не для всех задач подходит state machine”.

    Напомнило статью Как два программиста хлеб пекли https://habr.com/ru/articles/153225/


  1. vadikun
    06.06.2026 06:17

    Интересно, а никого (включая автора) не смутило, что конечный автомат светофора на рисунке неверно составлен в исходной статье?))

    Конечно, каждая задача должна решаться с применением наиболее подходящих инструментов. Теория и алгоритмы конечных автоматов очень много где пуспешно рименяются, например, в системах массового обслуживания.

    В данном случае неудачный пример в исходной статье напоминает закручивание шурупов выключенным шуруповёртом как отверткой. А автор, пользуясь этим, склоняет некрепшие умы к тому, что математические методы не стоит применять в программировании, т.к. это слишком сложно. И где-то он безусловно прав, т.к. увлечение новомодными инструментами и паттернами уводит многих разработчиков в сторону, заставляя тратить время на архитектуру в проектах, которые функционально ограничены. И уж точно автор прав в том, что задачи архитектора, инженера, программиста очень сильно разичаются и требуют разных подходов и инструментов. Но теорию учить и применять надо!)


  1. MgKav
    06.06.2026 06:17

    Всё примеры программ, приведённые автором, являются реализациями конечного автомата. Есть состояния и условия переходов. Нейросетей не вижу. Как то так


  1. kmatveev
    06.06.2026 06:17

    Если оставить за скобками брюзжащую и умничающую манеру автора, то в статье он взял вырожденный пример, в котором из каждого состояния возможен только один выход, развернул этот пример линейно-циклично и громогласно объявил о своей победе. Светофор - самый тупой пример КМ, который можно было взять.