org.myire.util.FiniteStateMachine Maven / Gradle / Ivy
/*
* Copyright 2009, 2016 Peter Franzen. All rights reserved.
*
* Licensed under the Apache License v2.0: http://www.apache.org/licenses/LICENSE-2.0
*/
package org.myire.util;
import java.util.function.BiFunction;
import static java.util.Objects.requireNonNull;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import javax.annotation.concurrent.NotThreadSafe;
/**
* A Finite State Machine is defined by
*
* - a finite set of possible states, {@code S}
* - a initial state {@code s0}, which is a member of {@code S}
* - a finite set of recognized input symbols {@code I}, called the input alphabet
* - a finite set of output symbols {@code O}, called the output alphabet
* - a state transition function (also called the next function) {@code T: S x I -> S} that
* maps pairs of state and input symbol to states
* - an output function {@code G: S x I -> O} that maps pairs of state and input symbol to output
* symbols
*
* This implementation is a Mealy machine, meaning that the output is determined both by its
* current state and the current input (as opposed to a Moore machine whose output is
* determined solely by the current state).
*
* The concept of final (or accepting) states is not supported by this implementation. Users can
* however easily add this by comparing the FSM's current state to an externally defined set of
* final states when there is no more input.
*
* Whether or not {@code null} is valid state or a valid symbol in the input and output alphabets is
* a property of the next and output functions. The FSM itself puts no restrictions on using
* {@code null}.
*
* The possible states, input alphabet, and output alphabet are all parametrized types. There are
* no restrictions on these types, but the fact that that the sets are finite may imply that using
* {@code Enum} types is a good choice.
*
* Instances of this class are not safe for use by multiple threads without external
* synchronization.
*
* @param The type that defines the set of possible states for the FSM.
* @param The type that defines the input alphabet for the FSM.
* @param The type that defines the output alphabet.
*
* @author Peter Franzen
*/
@NotThreadSafe
public class FiniteStateMachine
{
private final S fInitialState;
private S fCurrentState;
private final BiFunction fNextFunction;
private final BiFunction fOutputFunction;
/**
* Create a new {@code FiniteStateMachine}.
*
* @param pInitialState The FSM's initial state.
* @param pNextFunction The FSM's next function.
* @param pOutputFunction The FSM's output function.
*
* @throws NullPointerException if any of the function parameters is null.
*/
public FiniteStateMachine(
@Nullable S pInitialState,
@Nonnull BiFunction pNextFunction,
@Nonnull BiFunction pOutputFunction)
{
fInitialState = pInitialState;
fCurrentState = pInitialState;
fNextFunction = requireNonNull(pNextFunction);
fOutputFunction = requireNonNull(pOutputFunction);
}
/**
* Get the current state of the FSM.
*
* @return The current state of the FSM.
*/
@Nullable
public S getCurrentState()
{
return fCurrentState;
}
/**
* Consume an input symbol and emit the result of the output function. The FSM will be
* transitioned into the appropriate state as defined by the state transition function. The
* new current state can be retrieved by calling {@link #getCurrentState()}.
*
* @param pInput The input symbol.
*
* @return The result of the output function.
*/
@Nullable
public O consumeAndEmit(@Nullable I pInput)
{
S aNextState = fNextFunction.apply(fCurrentState, pInput);
O aOutput = fOutputFunction.apply(fCurrentState, pInput);
fCurrentState = aNextState;
return aOutput;
}
/**
* Reset the FSM by setting its current state to the initial state. This is not a state
* transition; neither the next function nor the output function will be called, and no output
* will be emitted.
*/
public void reset()
{
fCurrentState = fInitialState;
}
}