учебные пособия по конечным автоматам

Мне просто интересно, знает ли кто-нибудь в Интернете несколько хороших руководств по разработке конечных автоматов. Или электронные книги?

Я начинаю работать над конечными автоматами, и мне просто нужно что-то общее, чтобы начать.


person ant2009    schedule 03.09.2009    source источник
comment
См. Также здесь: stackoverflow.com/questions/1647631/ c-state-machine-design /   -  person paxdiablo    schedule 30.10.2009


Ответы (8)


Конечные автоматы в C очень просты, если вы используете указатели на функции.

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

int entry_state(void);
int foo_state(void);
int bar_state(void);
int exit_state(void);

/* array and enum below must be in sync! */
int (* state[])(void) = { entry_state, foo_state, bar_state, exit_state};
enum state_codes { entry, foo, bar, end};

enum ret_codes { ok, fail, repeat};
struct transition {
    enum state_codes src_state;
    enum ret_codes   ret_code;
    enum state_codes dst_state;
};
/* transitions from end state aren't needed */
struct transition state_transitions[] = {
    {entry, ok,     foo},
    {entry, fail,   end},
    {foo,   ok,     bar},
    {foo,   fail,   end},
    {foo,   repeat, foo},
    {bar,   ok,     end},
    {bar,   fail,   end},
    {bar,   repeat, foo}};

#define EXIT_STATE end
#define ENTRY_STATE entry

int main(int argc, char *argv[]) {
    enum state_codes cur_state = ENTRY_STATE;
    enum ret_codes rc;
    int (* state_fun)(void);

    for (;;) {
        state_fun = state[cur_state];
        rc = state_fun();
        if (EXIT_STATE == cur_state)
            break;
        cur_state = lookup_transitions(cur_state, rc);
    }

    return EXIT_SUCCESS;
}

Я не ставлю lookup_transitions() функцию, это банально.

Так я годами занимаюсь государственными автоматами.

person qrdl    schedule 03.09.2009
comment
Здорово, спасибо за это. Единственный возможный недостаток здесь заключается в том, что lookup_transitions, вероятно, будет линейным (O (n)) с этой структурой данных таблицы переходов. Его можно улучшить с помощью многомерного массива - гарантированный O (1). Например. таблица может быть представлена ​​как многомерный массив, где ключ - это состояние, а значение - это массив, где ключ - это код возврата, а значение - это следующее состояние: int state_transitions[][3] = { [entry] = { foo, end, foo }, ... } /* ok, fail, repeat */ - person Alex; 11.10.2013
comment
Почему ваши функции состояния не возвращают перечисление вместо целых чисел для функции поиска? Вы возвращаете код возврата, не так ли? - person Django; 03.04.2016
comment
Разве не было бы проще использовать src_state и dst_state в качестве указателей на функции? Я не понимаю преимущества наличия у них типа перечисления, когда все, что вы используете, - это поиск указателей на некоторые функции в массиве. - person Multisync; 17.08.2016
comment
@Multisync Вообще говоря, state! = Function имеет несколько разных состояний, которые фактически используют одну и ту же функцию. Например, у вас может быть несколько функций, которые готовят сообщение, одна функция для его отправки и одна функция для получения ответа, но эти две функции ввода-вывода будут использоваться в разных состояниях. - person qrdl; 17.08.2016
comment
Любое состояние можно рассматривать как одну sub_main_function, из-за действий в этих sub_main_functions, оно может снова перейти в другое состояние, давайте использовать переключатель, каждое состояние case: есть несколько функций рядом, кто-нибудь со мной? - person Fuevo; 14.10.2019

Я предпочитаю использовать указатели на функции, а не гигантские операторы switch, но, в отличие от ответа qrdl, я обычно не используйте явные коды возврата или таблицы переходов.

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

#include <stdio.h>

struct state;
typedef void state_fn(struct state *);

struct state
{
    state_fn * next;
    int i; // data
};

state_fn foo, bar;

void foo(struct state * state)
{
    printf("%s %i\n", __func__, ++state->i);
    state->next = bar;
}

void bar(struct state * state)
{
    printf("%s %i\n", __func__, ++state->i);
    state->next = state->i < 10 ? foo : 0;
}

int main(void)
{
    struct state state = { foo, 0 };
    while(state.next) state.next(&state);
}
person Christoph    schedule 05.09.2009
comment
Ваш main не возвращает значение. . . должно это? - person dreamlax; 20.11.2009
comment
@dreamlax: C99 5.1.2.2.3: достижение конца main() неявно возвращает 0 - person Christoph; 20.11.2009
comment
Очень понравился этот ответ. Просто и понятно. Молодец. - person AntonioCS; 14.02.2015
comment
Извините, я правда не понимаю. Что происходит в условии while в последней строке? Вы звоните foo()? Какие аргументы по умолчанию используются, если они не указаны? - person Multisync; 16.08.2016
comment
@Multisync Инициализатор структуры в предыдущей строке устанавливает state.next (указатель функции) на адрес foo. Итак, state.next(&state) совпадает с foo(&state) (первая итерация цикла, позже она указывает на другое место). Для сравнения, если бы это был C ++, foo() был бы членом класса State (State::foo()) и не принимал бы никаких параметров. Его тело будет использовать this->next = bar вместо state->next = bar. В C вы должны явно передать эквивалент указателя this, поскольку нет областей классов с отслеживанием состояния. - person Dan Bechard; 19.01.2017
comment
Разве этот способ не потребляет больше оперативной памяти, чем просто использование большого шаблона if, else if, else? Будет ли это безвредно для встроенных систем? - person MrBit; 12.02.2018

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

Тем не менее, довольно легко реализовать конечные автоматы на C, используя либо операторы switch для отправки событий состояниям (для простых автоматов они в значительной степени кодируют прямо), либо использование таблиц для сопоставления событий с переходами состояний.

Здесь есть пара простых, но достойных статей по базовой структуре для конечных автоматов на C:

Изменить: сайт «на обслуживании», ссылки на веб-архив:

switch конечные автоматы на основе операторов часто используют набор макросов, чтобы «скрыть» механизм оператора switch (или использовать набор операторов _3 _ / _ 4 _ / _ 5_ вместо switch) и сделать то, что составляет «язык конечных автоматов» для описание конечного автомата в исходном коде C. Я лично предпочитаю табличный подход, но он определенно имеет свои достоинства, широко используется и может быть эффективным, особенно для более простых автоматов.

Одна такая структура описана Стивом Рабином в " Жемчужины игрового программирования "Глава 3.0 (Разработка общего надежного движка ИИ).

Здесь обсуждается аналогичный набор макросов:

Если вас также интересуют реализации конечного автомата C ++, можно найти гораздо больше. Я отправлю указатели, если вам интересно.

person Michael Burr    schedule 03.09.2009
comment
Спасибо, это были хорошие статьи. Я тоже нашел здесь. en.wikipedia.org/wiki/Event_driven_finite_state_machine - person ant2009; 03.09.2009

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

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

Код написан с использованием C ++ и доступен как ParseFCU. Как видите, сначала он определяет, какую версию мы анализируем, и оттуда он входит в два разных конечных автомата.

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

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

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

person X-Istence    schedule 03.09.2009
comment
Просто любопытно - как воздушный шар приближается к космосу? Разве воздушным шарам не нужна атмосфера? Разве вы не дойдете до того момента, когда будет трудно / невозможно вернуться? - person Chris Lutz; 03.09.2009
comment
@Chris: Ближний космос определяется как все, что выше 60000 футов, наш воздушный шар достиг высоты 92999 футов. В какой-то момент латексный шар начинает становиться настолько большим из-за декомпрессии (газ продолжает расширять воздушный шар), что воздушный шар лопается, при этом наведите на ближний космический корабль, свободно падающий обратно на землю (мы прикрепляем парашют, отклоняясь от курса), см. связанную Wiki для получения дополнительной информации о наших усилиях в ближнем космосе и Google вокруг, это абсолютно потрясающее хобби! - person X-Istence; 03.09.2009
comment
Это звучит как дико неэффективное, смехотворно дорогое, возможно, немного расточительное и совершенно потрясающее хобби. - person Chris Lutz; 03.09.2009
comment
Многие мощные и важные эксперименты были проведены с баллонных платформ, вы должны сравнить затраты с затратами на запуск эквивалентной орбитальной платформы. К тому времени, когда вы достигнете высоты около 100000 футов, ваша проблема управления теплом станет существенной для космического корабля: кондуктивный и конвективный нагрев / охлаждение исчезают по сравнению с излучением. - person dmckee --- ex-moderator kitten; 03.09.2009
comment
@Chris: У нас был бюджет в 2000 долларов для работы, и мы успешно запустили два шара. Самой дорогой частью был гелий для заполнения латексных шаров, которые были второй по стоимости частью. - person X-Istence; 04.09.2009
comment
@ChrisLutz Я бы сказал с точностью до наоборот. По сравнению с альтернативой: ракеты; Это намного эффективнее, дешевле и значительно менее расточительно, но немного менее круто. - person Dan Bechard; 19.01.2017
comment
Забавно, что обсуждение перешло от указателей функций к выноскам. !! :) - person scooby; 10.03.2017
comment
Ссылки не работают. См. Следующий репозиторий Github для проекта: github .com / bertjwregeer / bertjwregeer.com / blob / master / input / - person Stephen305; 11.06.2019

Это все, что вам нужно знать.

int state = 0;
while (state < 3)
{
    switch (state)
    {
        case 0:
            // Do State 0 Stuff
            if (should_go_to_next_state)
            {
                state++;
            }
            break;
        case 1:
            // Do State 1 Stuff    
            if (should_go_back) 
            {
                state--;
            }    
            else if (should_go_to_next_state) 
            {
                state++;
            }
            break;
        case 2:
            // Do State 2 Stuff    
            if (should_go_back_two) 
            {
                state -= 2;
            }    
            else if (should_go_to_next_state) 
            {
                state++;
            }
            break;
        default:
            break;
    }
}
person ChaosPandion    schedule 03.09.2009
comment
Я считаю, что лучше явно задавать состояние, но это только я. - person Chris Lutz; 03.09.2009
comment
Было бы лучше называть государство. Позвольте мне это исправить. - person ChaosPandion; 03.09.2009
comment
Вы многое упустили, например: подсостояния; события и комбинации событие / состояние; диаграммы переходов состояний; охранники (не менять состояние, если); методы входа в состояние и выхода из состояния (при выходе из этого состояния выполните следующий метод). - person ChrisW; 03.09.2009
comment
Это чрезмерное упрощение конечных автоматов и не такой уж хороший пример. - person X-Istence; 03.09.2009
comment
Не усложняйте что-то очень простое. - person ChaosPandion; 03.09.2009
comment
Страж - это условие, которое должно выполняться, чтобы пройти переход. Хммм ... это сложный способ сказать оператор if. - person ChaosPandion; 03.09.2009
comment
Конечно, в качестве скелета того, как может выглядеть базовый конечный автомат, этого может быть достаточно. Но я думаю, что это еще не все, что вам нужно знать. Кроме того, вы можете сделать свое состояние неподписанным. - person mrduclaw; 03.09.2009
comment
Помимо маркировки состояний perhpaps, это охватывает большинство простых реализаций конечного автомата, с которыми я столкнулся. Некоторым нравится усложнять его, чтобы продемонстрировать свои языковые навыки. Будьте проще, если это просто ... - person Ashley Duncan; 09.03.2018

Объектно-ориентированное моделирование в реальном времени было фантастическим ( опубликовано в 1994 году и сейчас продается всего за 81 цент плюс 3,99 доллара за доставку).

person ChrisW    schedule 03.09.2009
comment
1 новый от 60,20 доллара и 24 б / у от 0,81 доллара. Это довольно забавно. - person marcc; 03.09.2009
comment
Интересно, что реферер по этой ссылке - StackOverflow. - person Carl; 31.01.2014

Есть много уроков для изучения ручного создания конечных автоматов на C, но позвольте мне также предложить компилятор конечных автоматов Ragel:

http://www.complang.org/ragel/

Он имеет довольно простой способ определения конечных автоматов, а затем вы можете генерировать графики, генерировать код в разных стилях (на основе таблиц, на основе goto), анализировать этот код, если хотите, и т. Д. И он мощный, может использоваться в производстве код для различных протоколов.

person Roman Khimov    schedule 03.09.2009

Конечные автоматы могут быть очень сложными для сложной задачи. Они также подвержены неожиданным ошибкам. Они могут превратиться в кошмар, если кто-то столкнется с ошибкой или потребуется изменить логику в будущем. Их также трудно отслеживать и отлаживать без диаграммы состояний. Структурированное программирование намного лучше (например, вы, вероятно, не будете использовать конечный автомат на уровне основной линии). Вы можете использовать структурированное программирование даже в контексте прерывания (где обычно используются конечные автоматы). См. Эту статью «Макросы для моделирования нескольких код задания / блокировки на уровне прерывания " на codeproject.com.

person eddyq    schedule 26.04.2016
comment
Не отвечает на вопрос. Вместо этого идет статья о том, почему конечные автоматы плохи. - person the_endian; 22.03.2020
comment
Проголосовали за близость к единственно правильному ответу, хотя точная формулировка и предоставленная ссылка неуместны. Теория автоматов, включающая в себя конечные автоматы, представляет собой математическую модель вычислений, не имеющую прямого практического применения. Мы используем языки программирования, которые считаются полными по Тьюрингу на компьютерах, которые сами являются конечными автоматами, и мы бы не использовали их для моделирования машины Тьюринга, не так ли? Тогда зачем нам моделировать ограниченную под-концепцию? Алгоритмы, описанные другими ответами на этой странице, являются замедлением и признаком менталитета толпы в программировании. - person Theo d'Or; 20.03.2021