it-roy-ru.com

использует для конечных автоматов

В каких областях программирования я бы использовал конечные автоматы? Зачем ? Как я мог реализовать один?

Правка: пожалуйста, приведите практический пример, если не слишком много, чтобы спросить.

63
Geo

В каких областях программирования я бы использовал конечный автомат?

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

Зачем мне использовать конечный автомат?

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

Как я могу реализовать один?

Тривиальный пример:

enum states {      // Define the states in the state machine.
  NO_PIZZA,        // Exit state machine.
  COUNT_PEOPLE,    // Ask user for # of people.
  COUNT_SLICES,    // Ask user for # slices.
  SERVE_PIZZA,     // Validate and serve.
  EAT_PIZZA        // Task is complete.
} STATE;

STATE state = COUNT_PEOPLE;
int nPeople, nSlices, nSlicesPerPerson;

// Serve slices of pizza to people, so that each person gets
/// the same number of slices.   
while (state != NO_PIZZA)  {
   switch (state)  {
   case COUNT_PEOPLE:  
       if (promptForPeople(&nPeople))  // If input is valid..
           state = COUNT_SLICES;       // .. go to next state..
       break;                          // .. else remain in this state.
   case COUNT_SLICES:  
       if (promptForSlices(&nSlices))
          state = SERVE_PIZZA;
        break;
   case SERVE_PIZZA:
       if (nSlices % nPeople != 0)    // Can't divide the pizza evenly.
       {                             
           getMorePizzaOrFriends();   // Do something about it.
           state = COUNT_PEOPLE;      // Start over.
       }
       else
       {
           nSlicesPerPerson = nSlices/nPeople;
           state = EAT_PIZZA;
       }
       break;
   case EAT_PIZZA:
       // etc...
       state = NO_PIZZA;  // Exit the state machine.
       break;
   } // switch
} // while

Примечания:

  • В этом примере для простоты используется switch() с явными case/break состояниями. На практике case часто "проваливается" в следующее состояние.

  • Для простоты обслуживания большого конечного автомата работу, выполняемую в каждом case, можно инкапсулировать в "рабочую" функцию. Получите любые входные данные в верхней части while(), передайте их рабочей функции и проверьте возвращаемое значение рабочего для вычисления следующего состояния.

  • Для компактности вся switch() может быть заменена массивом указателей на функции. Каждое состояние воплощается функцией, возвращаемым значением которой является указатель на следующее состояние. Предупреждение: Это может либо упростить конечный автомат, либо сделать его полностью не поддерживаемым, поэтому внимательно рассмотрите реализацию!

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

80
Adam Liss

Некоторые отличные ответы уже. Для немного другой перспективы, рассмотрите поиск текста в большей строке. Кто-то уже упоминал о регулярных выражениях, и это действительно особый случай, хотя и важный.

Рассмотрим следующий вызов метода:

very_long_text = "Bereshit bara Elohim et hashamayim ve'et ha'arets." …
Word = "Elohim"
position = find_in_string(very_long_text, Word)

Как бы вы реализовали find_in_string? Простой подход будет использовать вложенный цикл, что-то вроде этого:

for i in 0 … length(very_long_text) - length(Word):
    found = true
    for j in 0 … length(Word):
        if (very_long_text[i] != Word[j]):
            found = false
            break
    if found: return i
return -1

Помимо того, что это неэффективно, это формирует конечный автомат! Состояния здесь несколько скрыты; позвольте мне немного переписать код, чтобы сделать его более заметным:

state = 0
for i in 0 … length(very_long_text) - length(Word):
    if very_long_text[i] == Word[state]:
        state += 1
        if state == length(Word) + 1: return i
    else:
        state = 0
return -1

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

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

21
Konrad Rudolph

Что за задание?

Любая задача, кроме того, что я видел, разбор любого рода часто реализуется как конечный автомат.

Почему?

Разбор грамматики, как правило, не простая задача. На этапе проектирования довольно часто для проверки алгоритма разбора рисуется диаграмма состояний. Перевод этого в конечный автомат - довольно простая задача.

Как?

Ну, вы ограничены только вашим воображением.

Я видел, как это делается с помощью операторы case и циклы .

Я видел это сделано с помощью метки и goto заявления

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

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

int a[10] = {some unsorted integers};

not_sorted_state:;
    z = -1;
    while (z < (sizeof(a) / sizeof(a[0]) - 1)
    {
        z = z + 1
        if (a[z] > a[z + 1])
        {
            // ASSERT The array is not in order
            swap(a[z], a[z + 1];        // make the array more sorted
            goto not_sorted_state;      // change state to sort the array
        }
    }
    // ASSERT the array is in order

Там нет переменных состояния, но сам код представляет состояние

13
EvilTeach

Шаблон проектирования State - это объектно-ориентированный способ представления состояния объекта с помощью конечного автомата. Обычно это помогает уменьшить логическую сложность реализации этого объекта (вложенные if, много флагов и т.д.)

8
Federico A. Ramponi

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

Если вы используете .NET, попробуйте Windows Workflow Foundation. Вы можете довольно быстро реализовать рабочий процесс конечного автомата.

6
Maxam

Если вы используете C #, каждый раз, когда вы пишете блок итератора, вы просите компилятор создать для вас конечный автомат (отслеживание того, где вы находитесь в итераторе и т.д.).

4
Jon Skeet

Вот проверенный и работающий пример конечного автомата. Допустим, вы находитесь в последовательном потоке (типичные примеры - последовательный порт, данные tcp/ip или файл). В этом случае я ищу конкретную структуру пакета, которая может быть разбита на три части: синхронизация, длина и полезная нагрузка. У меня есть три состояния: одно неактивно, ожидание синхронизации, второе - хорошая синхронизация, следующий байт должен быть длиной, а третье - накапливать полезную нагрузку.

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

Я использую этот тип конечного автомата все время, будь то последовательные потоки данных, TCP/IP, файловый ввод/вывод. Или, возможно, сами протоколы tcp/ip, скажем, вы хотите отправить электронное письмо, открыть порт, подождать, пока сервер отправит ответ, отправить HELO, подождать, пока сервер отправит пакет, отправить пакет, дождаться ответа, и т. д. По существу, в этом случае, а также в приведенном ниже, вы можете бездействовать, ожидая поступления следующего байта/пакета. Чтобы запомнить то, что вы ожидали, также повторно использовать код, который ожидает чего-то, что вы можете использовать переменные состояния. Точно так же, как конечные автоматы используются в логике (ожидание следующих часов, чего я ждал).

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

Я надеюсь, что это полезно, и желаю, чтобы конечные автоматы были больше использованы в программном обеспечении.

У данных теста есть преднамеренные проблемы, с которыми восстанавливается конечный автомат. После первого хорошего пакета есть некоторые данные мусора, пакет с неверной контрольной суммой и пакет с недопустимой длиной. Мой вывод был:

исправный пакет: FA0712345678EB Неверный шаблон синхронизации 0x12 Неверный шаблон синхронизации 0x34 Неверный шаблон синхронизации 0x56 Ошибка контрольной суммы 0xBF Неверная длина пакета 0 Неверный шаблон синхронизации 0x12 Неверный шаблон синхронизации 0x34 Неверный шаблон синхронизации 0x56 Неверный шаблон синхронизации 0x78 Неверный шаблон Синхронизации 0 0 0 0 0 Неправильный пакет синхронизации 0x00 Неправильный пакет синхронизации 0 0 0 0 Не более данные

Два хороших пакета в потоке были извлечены, несмотря на неверные данные. И плохие данные были обнаружены и обработаны.

 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>

unsigned char testdata[] =
{
    0xFA,0x07,0x12,0x34,0x56,0x78,0xEB,  
    0x12,0x34,0x56,  
    0xFA,0x07,0x12,0x34,0x56,0x78,0xAA,  
    0xFA,0x00,0x12,0x34,0x56,0x78,0xEB,  
    0xFA,0x08,0x12,0x34,0x56,0x78,0x00,0xEA  
};

unsigned int testoff=0;

//packet structure  
// [0] packet header 0xFA  
// [1] bytes in packet (n)  
// [2] payload  
// ... payload  
// [n-1] checksum  
//  

unsigned int state;

unsigned int packlen;  
unsigned int packoff;  
unsigned char packet[256];  
unsigned int checksum;  

int process_packet( unsigned char *data, unsigned int len )  
{  
    unsigned int ra;  

    printf("good packet:");
    for(ra=0;ra<len;ra++) printf("%02X",data[ra]);
    printf("\n");
}  
int getbyte ( unsigned char *d )  
{  
    //check peripheral for a new byte  
    //or serialize a packet or file  

    if(testoff<sizeof(testdata))
    {
        *d=testdata[testoff++];
        return(1);
    }
    else
    {
        printf("no more test data\n");
        exit(0);
    }
    return(0);
}

int main ( void )  
{  
    unsigned char b;

    state=0; //idle

    while(1)
    {
        if(getbyte(&b))
        {
            switch(state)
            {
                case 0: //idle
                    if(b!=0xFA)
                    {
                        printf("Invalid sync pattern 0x%02X\n",b);
                        break;
                    }
                    packoff=0;
                    checksum=b;
                    packet[packoff++]=b;

                    state++;
                    break;
                case 1: //packet length
                    checksum+=b;
                    packet[packoff++]=b;

                    packlen=b;
                    if(packlen<3)
                    {
                        printf("Invalid packet length %u\n",packlen);
                        state=0;
                        break;
                    }

                    state++;
                    break;
                case 2: //payload
                    checksum+=b;
                    packet[packoff++]=b;

                    if(packoff>=packlen)
                    {
                        state=0;
                        checksum=checksum&0xFF;
                        if(checksum)
                        {
                            printf("Checksum error 0x%02X\n",checksum);
                        }
                        else
                        {
                            process_packet(packet,packlen);
                        }
                    }
                    break;
            }
        }

        //do other stuff, handle other devices/interfaces

    }
}
4
old_timer

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

2
Nate

Инфраструктура обеспечения качества, предназначенная для проверки или иного прохождения тестируемого процесса. (Это мой конкретный опыт; я построил инфраструктуру конечного автомата в Python для моего последнего работодателя с поддержкой переноса текущего состояния в стек и использованием различных методов выбора обработчика состояний для использования во всех наших Основанные на TTY скребки экрана). Концептуальная модель хорошо подходит, поскольку, проходя через приложение TTY, она проходит через ограниченное число известных состояний и может быть возвращена к старым (подумайте об использовании вложенного меню). Это было выпущено (с указанного разрешения работодателя); используйте Bazaar , чтобы проверить http://web.dyfis.net/bzr/isg_state_machine_framework/, если вы хотите увидеть код.

Системы управления билетами, процессами и рабочими процессами - если в вашем билете есть набор правил, определяющих его движение между NEW, TRIAGED, IN-PROGRESS, NEEDS-QA, FAILED-QA и VERIFIED (например), вы получаете простой конечный автомат.

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

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

2
Charles Duffy

Я не увидел здесь ничего, что могло бы объяснить причину, по которой я их использовал.

В практических целях программисту обычно приходится добавлять его, когда он вынужден возвращать поток/выход прямо в середине операции.

Например, если у вас HTTP-запрос с несколькими состояниями, у вас может быть серверный код, который выглядит следующим образом:

Show form 1
process form 1
show form 2
process form 2

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

Ввод кода в коде и возвращение потока обычно выполняется с помощью оператора switch и создает так называемый конечный автомат (Very Basic Version).

По мере того, как вы становитесь более сложными, может быть действительно сложно определить, какие состояния допустимы. Затем люди обычно определяют " Таблица переходов состояний ", чтобы описать все переходы состояний.

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

2
Bill K

Регулярные выражения - это еще один пример того, как в игру вступают конечные автоматы (или "конечные автоматы").

Скомпилированное регулярное выражение - это конечный автомат, а наборы строк, которым могут соответствовать регулярные выражения, - это именно те языки, которые могут принимать конечные автоматы (называемые "обычными языками").

2
Federico A. Ramponi

Многие разработки цифрового оборудования включают создание конечных автоматов для определения поведения ваших цепей. Если вы пишете VHDL, то это совсем немного.

2
Ryan Fox

FSM используется везде, где у вас есть несколько состояний, и вам нужно перейти в другое состояние по стимулу.

(оказывается, это охватывает большинство проблем, по крайней мере, теоретически)

2
Paul Nathan

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

1
dviljoen

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

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

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

Если вы хотите узнать больше, вы можете проверить Морфология конечного состояния Бисли и Карттунена, а также инструментарий конечного состояния Xerox, разработанный им в PARC.

1
Robert Elwell

Типичный пример использования - светофор.

На заметку о реализации: Java 5 перечисления могут иметь абстрактные методы, что является отличным способом инкапсулировать поведение, зависящее от состояния.

0
thSoft

Хорошие ответы. Вот мои 2 цента. Конечные автоматы - это теоретическая идея, которая может быть реализована множеством разных способов, таких как таблица, или как переключатель времени (но не говорите никому, что это способ сказать goto horrors ). Это теорема, что любой FSM соответствует регулярному выражению, и наоборот. Поскольку регулярное выражение соответствует структурированной программе, вы можете иногда просто написать структурированную программу для реализации своего FSM. Например, простой синтаксический анализатор чисел может быть записан следующим образом:

/* implement dd*[.d*] */
if (isdigit(*p)){
    while(isdigit(*p)) p++;
    if (*p=='.'){
        p++;
        while(isdigit(*p)) p++;
    }
    /* got it! */
}

Вы поняли идею. И, если есть способ, который работает быстрее, я не знаю, что это такое.

0
Mike Dunlavey

Управляемый состоянием код является хорошим способом реализации определенных типов логики (например, парсеры). Это можно сделать несколькими способами, например:

  • Управление состоянием, какой бит кода фактически выполняется в данной точке (т. Е. Состояние неявно присутствует в куске кода, который вы пишете). парсеры рекурсивного спуска являются хорошим примером этого типа кода.

  • Состояние вождения, что делать в условных выражениях, таких как оператор switch.

  • Явные конечные автоматы, такие как сгенерированные инструментами генерации синтаксического анализатора, такими как Lex и Yacc .

Не весь управляемый состоянием код используется для анализа. Генератор конечных автоматов smc . Он вдыхает определение конечного автомата (на его языке) и выдает код конечного автомата на разных языках.

0
ConcernedOfTunbridgeWells