1.0.0 • Published 5 years ago

@klauss.net/statemachine v1.0.0

Weekly downloads
-
License
MIT
Repository
-
Last release
5 years ago

State Machine

A Finite State Machine is an abstract machine used to represent the flow of an algorithm in sequential, easy to follow steps that will allow readers to understand the algorithm's implementation, function and purpose. Using Finite State Machines, you develop flexible algorithms without having to understand the ins and outs of said algorithm. Simply construct a State Machine, specify the states and logic around them, give some input and you've got your algorithm implemented. Of course a solid understanding of these abstract machines is necessary before diving into this (arguably, simple) library.

We've written a little article about these State Machines and their function for you as well as documentation on the library itself.

What is a State Machine

As mentioned, a State Machine is an abstraction of a physical machine who's job is to express and sometimes even perform logical or state-based calculations based on particular inputs. In other words, they behave like switches that move depending on some factor.

They are generally represented graphically using simple rules: 1. All states are represented as circles. 2. All states are connected to each other using arrows. 3. Each state transition is conditional. 4. An accepting state is double-outlined. 5. State names are placed inside the circle of the state. 6. All labels on arrows are treated as conditions

Take this State Machine as an example:

Image of a state machine

As you can see, the circles represent states. The state A is an accepting one, more on that later.

How do they work

Taking another look at the example above, you'll notice that providing an input of 0 anywhere won't change the current state. Meaning if a 0 is encountered, the state will move to itself. However, should a 1 be encountered, the state will change.

To put it simply, this state machine will toggle between states A and B on a 1 input but won't change on a 0 input.

Fun Fact: State Machines are the basis of Regular Expressions. Without State Machines, a lot of pattern matching would be a lot more difficult.

An advanced enough state machine could theoretically do anything - Computers are just mighty state machines!

This State Machine library simply works by requiring the user to predetermine the states along with conditions that can be used to map inputs to outputs. The library can take the machine's shape in several formats. The recommended format is through an Object containing states as functions and declaring them as accepting seperately. To do this, you are required to provide an Object containing the state's name or identifier which is used by other states to navigate to it, and the value being essentially, a resolver function.

The code

An example of the above's machine's states would be

{
    'A': input => {
        if (input === 0)
            return 'A';
        else 
            return 'B';
    },
    'B': input => {
        if (input === 0)
            return 'A';
        else 
            return 'B';
    },        
}

As you can see, the resolver functions simply return the name of the state they jump to.

To initialise a state machine, you pass this object into the StateMachine constructor function line so

const StateMachine = require('finitestatemachine');
const SM = new StateMachine( { ... } );

This however is not the only way to declare states. You can declare them through an array like so:

new StateMachine( [ input => ... ] );

The advantage of this method is that states are automatically named by index and any states followed by a boolean variable type will have their acceptance values set to its value.

Declaring a state as accepting involves another step: passing the state's name to the constructor by name in an array.

new StateMachine( { ... }, ['A'] );

This will render state A as accepting.

Like everything, there are alternatives. You can pass an object containing the acceptance values of each state too:

new StateMachine( { ... }, { 'A': true } );

or better yet, you can pass a resolver function:

new StateMachine( { ... }, stateName => true );

Running

A statemachine instance also provides simplified functions for calculating states by inputs.

there are four three ways to calculate values:

  • run,
  • headless
  • step
  • all

the run and headless function takes an array of arguments which are called on the state resolvers who will decide the state to proceed to next.The difference being run returns the state's name upon completion, -1 if the state is not accepting whereas headless will only return whether it's accepting at all.

step is a broken down version of run. Rather than taking an array of arguments, it takes a single one. It too returns the new state's name or -1 if it does not exist.

all takes an array of arrays. Provide it a list of input sets to try and get back an array of the results.