Обновить:
Еще раз спасибо за примеры, они были очень полезны, и со следующим я не хочу ничего от них отнимать.
Разве приведенные в настоящее время примеры, насколько я понимаю их и конечных автоматов, не являются лишь половиной того, что мы обычно понимаем под конечным автоматом?
В том смысле, что примеры действительно меняют состояние, но это представлено только изменением значения переменной (и разрешением разных изменений значения в разных состояниях), в то время как обычно конечный автомат также должен изменять свое поведение, а поведение не (только) в смысл разрешения различных изменений значений для переменной в зависимости от состояния, но в смысле разрешения выполнения различных методов для разных состояний.
Или у меня неправильное представление о конечных автоматах и их общем использовании?
С уважением
Оригинальный вопрос:
Я нашел это обсуждение о конечных автоматах и блоках итераторов в c # и инструментах для создания конечных автоматов, а не о C #, так что я нашел много абстрактных вещей, но в качестве нуба все это немного сбивает с толку.
Так что было бы здорово, если бы кто-то мог предоставить пример исходного кода C #, который реализует простой конечный автомат, возможно, с 3,4 состояниями, просто чтобы понять его суть.
источник
Ответы:
Давайте начнем с этой простой диаграммы состояний:
У нас есть:
Вы можете преобразовать это в C # несколькими способами, такими как выполнение оператора switch для текущего состояния и команды или поиск переходов в таблице переходов. Для этого простого конечного автомата я предпочитаю таблицу переходов, которую очень легко представить с помощью
Dictionary
:Что касается личных предпочтений, мне нравится проектировать мои конечные автоматы с
GetNext
функцией для детерминированного возврата следующего состояния иMoveNext
функцией для изменения конечного автомата.источник
GetHashCode()
использования простых чисел.StateTransition
класс используется в качестве ключа в словаре, и важно равенство ключей. Два разных экземпляраStateTransition
следует считать равными, если они представляют один и тот же переход (например,CurrentState
иCommand
являются одинаковыми). Для реализации равенства вы должны переопределить,Equals
а такжеGetHashCode
. В частности, словарь будет использовать хеш-код, и два равных объекта должны возвращать один и тот же хеш-код. Вы также получите хорошую производительность, если не слишком много неравных объектов совместно используют один и тот же хэш-код, поэтомуGetHashCode
он реализован так, как показано.Возможно, вы захотите использовать один из существующих конечных автоматов с открытым исходным кодом. Например, bbv.Common.StateMachine можно найти по адресу http://code.google.com/p/bbvcommon/wiki/StateMachine . Он имеет очень интуитивно понятный свободный синтаксис и множество функций, таких как действия входа / выхода, действия перехода, охранники, иерархическая, пассивная реализация (выполняется в потоке вызывающей стороны) и активная реализация (собственный поток, в котором работает fsm, события добавляются в очередь).
На примере Джульетта определение конечного автомата становится очень простым:
Обновление : местоположение проекта перемещено по адресу : https://github.com/appccelerate/statemachine.
источник
Вот пример очень классического конечного автомата, моделирующего очень упрощенное электронное устройство (например, телевизор)
источник
private void DoNothing() {return;}
и заменил все экземпляры null наthis.DoNothing
. Имеет приятный побочный эффект возврата текущего состояния.States
вUnpowered, Standby, On
. Я рассуждаю так: если бы меня спросили, в каком состоянии находится мой телевизор, я бы сказал «Выкл», а не «Старт». Я тоже изменилсяStandbyWhenOn
иStandbyWhenOff
наTurnOn
иTurnOff
. Это делает код более интуитивно понятным, но мне интересно, существуют ли соглашения или другие факторы, которые делают мою терминологию менее подходящей.Некоторая бесстыдная самореклама здесь, но некоторое время назад я создал библиотеку под названием YieldMachine, которая позволяет описать конечный автомат ограниченной сложности очень простым и понятным способом. Например, рассмотрим лампу:
Обратите внимание, что этот конечный автомат имеет 2 триггера и 3 состояния. В коде YieldMachine мы пишем единый метод для всего поведения, связанного с состоянием, в котором мы совершаем ужасное злодеяние использования
goto
для каждого состояния. Триггер становится свойством или полем типаAction
, украшенным названным атрибутомTrigger
. Я прокомментировал код первого состояния и его переходы ниже; следующие состояния следуют той же схеме.Коротко и красиво, а!
Этот конечный автомат управляется простой отправкой ему триггеров:
Просто чтобы уточнить, я добавил несколько комментариев к первому состоянию, чтобы помочь вам понять, как это использовать.
Это работает, потому что компилятор C # фактически создал конечный автомат для каждого метода, который использует
yield return
. Эта конструкция обычно используется для ленивого создания последовательностей данных, но в этом случае нас на самом деле не интересует возвращаемая последовательность (которая в любом случае равна нулю), а поведение поведения, которое создается внутри.StateMachine
Базовый класс делает некоторые размышления о строительстве для присвоения кода для каждого[Trigger]
действия, которое устанавливаетTrigger
элемент и перемещает государственную машину вперед.Но вам не нужно понимать внутренности, чтобы иметь возможность использовать их.
источник
goto
между методамиgoto
переходить между методами? Я не понимаю, как это могло бы сработать. Нет,goto
это проблематично, потому что это приводит к процедурному программированию (это само по себе усложняет такие приятные вещи, как модульное тестирование), способствует повторению кода (замечено, как егоInvalidTrigger
нужно вставлять для каждого состояния?) И, наконец, усложняет выполнение программы. Сравните это с (большинством) другими решениями в этой теме, и вы увидите, что это единственное решение, в котором весь FSM выполняется одним способом. Этого обычно достаточно, чтобы вызвать беспокойство.goto
очень хорошо.goto
переключаться между функциями, но он не поддерживает функции? :) Вы правы, замечание "труднее следовать" - это более общаяgoto
проблема, в данном случае это не такая уж большая проблема.Вы можете кодировать блок итератора, который позволяет вам выполнить блок кода организованным способом. То, как блок кода разбит, на самом деле не должно соответствовать чему-либо, просто то, как вы хотите его кодировать. Например:
В этом случае, когда вы вызываете CountToTen, на самом деле ничего еще не выполняется. По сути, вы получаете генератор конечных автоматов, для которого вы можете создать новый экземпляр конечного автомата. Вы делаете это, вызывая GetEnumerator (). Получающийся IEnumerator фактически является конечным автоматом, который вы можете использовать, вызывая MoveNext (...).
Таким образом, в этом примере при первом вызове MoveNext (...) вы увидите «1», записанное в консоль, а при следующем вызове MoveNext (...) вы увидите 2, 3, 4 и затем 5, 6, 7, а затем 8, а затем 9, 10. Как видите, это полезный механизм для организации того, как все должно происходить.
источник
Я публикую здесь другой ответ, так как это конечные автоматы с другой точки зрения; очень наглядно
Мой оригинальный ответ - классический императивный код. Я думаю, что это довольно визуально, поскольку код идет из-за массива, который делает визуализацию конечного автомата простой. Недостатком является то, что вы должны написать все это. Ответ Ремоса облегчает написание кода, но гораздо менее нагляден. Есть третья альтернатива; действительно рисует конечный автомат.
Если вы используете .NET и можете использовать версию 4 среды выполнения, у вас есть возможность использовать действия конечного автомата рабочего процесса . По сути, они позволяют вам нарисовать конечный автомат (как на диаграмме Джульетты ) и заставить WF выполнить его за вас.
Для получения дополнительной информации см. Статью MSDN « Создание конечных автоматов с помощью Windows Workflow Foundation» и этот сайт CodePlex для последней версии.
Это вариант, который я бы всегда предпочел при ориентации на .NET, потому что его легко увидеть, изменить и объяснить не программистам; картинки стоят тысячи слов как говорится!
источник
Полезно помнить, что конечные автоматы являются абстракцией, и вам не нужны специальные инструменты для их создания, однако инструменты могут быть полезны.
Например, вы можете реализовать конечный автомат с функциями:
Эта машина охотилась на чаек и пыталась поразить их водяными шарами. Если он пропустит, он будет пытаться выстрелить до тех пор, пока не достигнет цели (может, с некоторыми реалистичными ожиданиями;)), иначе он будет злорадствовать в консоли. Это продолжает охотиться, пока это не из чаек, чтобы изводить.
Каждая функция соответствует каждому состоянию; состояния начала и окончания (или принятия ) не показаны. Там, вероятно, больше состояний, чем моделируется функциями. Например, после запуска воздушного шара машина действительно находится в другом состоянии, чем было до нее, но я решил, что сделать это различие нецелесообразно.
Обычный способ - использовать классы для представления состояний, а затем соединять их различными способами.
источник
Нашел этот замечательный учебник в Интернете, и он помог мне обернуть голову в конечные автоматы.
http://gamedevelopment.tutsplus.com/tutorials/finite-state-machines-theory-and-implementation--gamedev-11867
Учебное пособие не зависит от языка, поэтому его можно легко адаптировать к вашим потребностям в C #.
Кроме того, используемый пример (муравей ищет еду) легко понять.
Из учебника:
источник
Сегодня я в глубине государства Design Pattern. Я сделал и протестировал ThreadState, который равен (+/-) Threading в C #, как описано на рисунке из Threading в C #
Вы можете легко добавлять новые состояния, настраивать переходы из одного состояния в другое очень легко, потому что оно включено в реализацию состояний
Внедрение и использование в: Реализует .NET ThreadState с помощью State Design Pattern
источник
Я еще не пробовал внедрять FSM в C #, но все это звучит (или выглядит) очень сложно по сравнению с тем, как я работал с FSM в прошлом на языках низкого уровня, таких как C или ASM.
Я считаю, что метод, который я всегда знал, называется чем-то вроде «Итеративного цикла». В нем, по сути, имеется цикл while, который периодически выходит на основе событий (прерываний), а затем снова возвращается в основной цикл.
В обработчиках прерываний вы должны передать CurrentState и вернуть NextState, который затем перезаписывает переменную CurrentState в основном цикле. Вы делаете это до бесконечности, пока программа не закроется (или микроконтроллер не перезагрузится).
То, что я вижу, другие ответы выглядят очень сложными по сравнению с тем, как, на мой взгляд, ФШМ должен быть реализован; Его красота заключается в его простоте, и FSM может быть очень сложным со многими, многими состояниями и переходами, но они позволяют легко разбивать и переваривать сложный процесс.
Я понимаю, что мой ответ не должен включать другой вопрос, но я вынужден спросить: почему эти другие предлагаемые решения кажутся такими сложными?
Кажется, они похожи на попадание маленького гвоздя с помощью гигантской кувалды
источник
Какой бой StatePattern. Это соответствует вашим потребностям?
Я думаю, что его контекст связан, но его точно стоит попробовать.
http://en.wikipedia.org/wiki/State_pattern
Это позволяет вашим штатам решать, куда идти, а не «объектному» классу.
Bruno
источник
По моему мнению, конечный автомат предназначен не только для изменения состояний, но также (очень важно) для обработки триггеров / событий в определенном состоянии. Если вы хотите лучше понять шаблон проектирования конечного автомата, хорошее описание можно найти в книге Head First Design Patterns, стр. 320 .
Речь идет не только о состояниях внутри переменных, но и об обработке триггеров в различных состояниях. Отличная глава (и нет, упоминать об этом мне не нужно :-), которая содержит просто понятное объяснение.
источник
Я только что добавил это:
https://code.google.com/p/ysharp/source/browse/#svn%2Ftrunk%2FStateMachinesPoC
Вот один из примеров, демонстрирующих прямую и непрямую отправку команд с состояниями как IObserver (для сигнала), таким образом, отвечает на источник сигнала, IObservable (для сигнала):
Примечание: этот пример довольно искусственный и в основном предназначен для демонстрации ряда ортогональных функций. Там должна быть редко реальная потребность в реализации самого состояния области значений полным взорван классом, используя CRTP (см http://en.wikipedia.org/wiki/Curiously_recurring_template_pattern ) , как это.
Вот для гораздо более простого и, вероятно, гораздо более распространенного варианта использования реализации (с использованием простого типа enum в качестве области значений состояний), для того же конечного автомата и с тем же тестовым примером:
https://code.google.com/p/ysharp/source/browse/trunk/StateMachinesPoC/WatchingTVSample.cs
«НТН
источник
StateChange
решается? Через отражение? Это действительно необходимо?private Television(string moniker, Television value) { Handler<Television, TvOperation, DateTime> myHandler = StateChange; // (code omitted) new { From = Television.Unplugged, When = TvOperation.Plug, Goto = Television.Off, With = myHandler } }
Я сделал этот универсальный конечный автомат из кода Джульетты. Это работает потрясающе для меня.
Вот эти преимущества:
TState
иTCommand
,TransitionResult<TState>
для большего контроля над результатами вывода[Try]GetNext()
методовStateTransition
только за счетAddTransition(TState, TCommand, TState)
упрощения работы с нимКод:
Это тип возврата метода TryGetNext:
Как пользоваться:
Вот как вы можете создать
OnlineDiscountStateMachine
из универсального класса:Определите перечисление
OnlineDiscountState
для его состояний и перечислениеOnlineDiscountCommand
для его команд.Определите класс,
OnlineDiscountStateMachine
производный от универсального класса, используя эти два перечисленияПолучите конструктор,
base(OnlineDiscountState.InitialState)
чтобы начальное состояние было установленоOnlineDiscountState.InitialState
Используйте
AddTransition
столько раз, сколько необходимоиспользовать производный конечный автомат
источник
Я думаю, что конечный автомат, предложенный Джульеттой, имеет ошибку: метод GetHashCode может возвращать один и тот же хэш-код для двух разных переходов, например:
Чтобы избежать этой ошибки, метод должен быть таким:
Alex
источник
int
значения). Вот почемуHashCode
всегда реализуется вместе сEquals
. Если хеш-коды одинаковы, то объекты проверяются на точную корректность с помощьюEquals
метода.FiniteStateMachine - это простой конечный автомат, написанный на C # Link.
Преимущества использования моей библиотеки FiniteStateMachine:
Скачать DLL Скачать
Пример на LINQPad:
источник
Я бы порекомендовал state.cs . Я лично использовал state.js (версия JavaScript) и очень доволен этим. Эта версия C # работает аналогичным образом.
Вы создаете экземпляры состояний:
Вы создаете несколько переходов:
Вы определяете действия над состояниями и переходами:
И это (в значительной степени) это. Посмотрите на сайте для получения дополнительной информации.
источник
В NuGet есть 2 популярных пакета конечных автоматов.
Appccelerate.StateMachine (13.6K загрузок + 3.82K устаревшей версии (bbv.Common.StateMachine))
StateMachineToolkit (1.56K загрузок)
В библиотеке Appccelerate lib есть хорошая документация , но она не поддерживает .NET 4, поэтому я выбрал StateMachineToolkit для своего проекта.
источник
Другая альтернатива в этом репо https://github.com/lingkodsoft/StateBliss использует свободный синтаксис, поддерживает триггеры.
источник
Вы можете использовать мое решение, это наиболее удобный способ. Это также бесплатно.
Создайте конечный автомат в три этапа:
1. Создайте схему в редакторе узлов и загрузите ее в свой проект, используя библиотеку.
2. Опишите логику своего приложения на событиях
3. Запустить конечный автомат🚘
Ссылки:
Редактор узлов: https://github.com/SimpleStateMachine/SimpleStateMachineNodeEditor
Библиотека: https://github.com/SimpleStateMachine/SimpleStateMachineLibrary
источник