Я не согласен с тем, что FSM тривиально реализовать. Это очень недальновидно и показывает либо недостаточное знакомство с альтернативами, либо отсутствие опыта работы со сложными конечными автоматами.
Основная проблема заключается в том, что конечный автомат graph очевиден, а FSM code - нет. Как только вы преодолеете дюжину состояний и наберете множество переходов, код FSM станет уродливым и трудным для отслеживания.
Существуют инструменты, с помощью которых вы рисуете конечный автомат и генерируете для него Java-код. Однако я не знаю ни одного инструмента с открытым исходным кодом для этого.
Теперь, возвращаясь к Erlang / Scala, в Scala есть также актеры и сообщения, и он основан на JVM, так что это может быть лучшей альтернативой, чем Erlang, учитывая ваши ограничения.
В Scala также есть библиотека DFA / NFA, хотя она не особенно хороша. Он поддерживает преобразование из произвольных регулярных выражений (т.е. литералы не обязательно должны быть символами) в DFA / NFA.
Я выложу код ниже, используя его. В этом коде идея состоит в том, чтобы создать FSM, который будет принимать любую последовательную комбинацию произвольных префиксов для списка слов, идея заключается в поиске параметров меню без предопределенных сочетаний клавиш.
import scala.util.regexp._
import scala.util.automata._
// The goal of this object below is to create a class, MyChar, which will
// be the domain of the tokens used for transitions in the DFA. They could
// be integers, enumerations or even a set of case classes and objects. For
// this particular code, it's just Char.
object MyLang extends WordExp {
type _regexpT = RegExp
type _labelT = MyChar
case class MyChar(c:Char) extends Label
}
// We now need to import the types we defined, as well as any classes we
// created extending Label.
import MyLang._
// We also need an instance (singleton, in this case) of WordBerrySethi,
// which will convert the regular expression into an automatum. Notice the
// language being used is MyLang.
object MyBerrySethi extends WordBerrySethi {
override val lang = MyLang
}
// Last, a function which takes an input in the language we defined,
// and traverses the DFA, returning whether we are at a sink state or
// not. For other uses it will probably make more sense to test against
// both sink states and final states.
def matchDet(pat: DetWordAutom[MyChar], seq: Seq[Char]): Boolean =
!pat.isSink((0 /: seq) ((state, c) => pat.next(state, MyChar(c))))
// This converts a regular expression to a DFA, with using an intermediary NFA
def compile(pat: MyLang._regexpT) =
new SubsetConstruction(MyBerrySethi.automatonFrom(pat, 100000)).determinize
// Defines a "?" function, since it isn't provided by the library
def Quest(rs: _regexpT*) = Alt(Eps, Sequ(rs: _*)) // Quest(pat) = Eps|pat = (pat)?
// And now, the algorithm proper. It splits the string into words
// converts each character into Letter[MyChar[Char]],
// produce the regular expression desired for each word using Quest and Sequ,
// then the final regular expression by using Sequ with each subexpression.
def words(s : String) = s.split("\\W+")
def wordToRegex(w : String) : Seq[MyLang._regexpT] = w.map(c => Letter(MyChar(c)))
def wordRegex(w : String) = Quest(wordToRegex(w) reduceRight ((a,b) => Sequ(a, Quest(b))))
def phraseRegex(s : String) = Sequ(words(s).map(w => wordRegex(w)) : _*)
// This takes a list of strings, produce a DFA for each, and returns a list of
// of tuples formed by DFA and string.
def regexList(l : List[String]) = l.map(s => compile(phraseRegex(s)) -> s)
// The main function takes a list of strings, and returns a function that will
// traverse each DFA, and return all strings associated with DFAs that did not
// end up in a sink state.
def regexSearcher(l : List[String]) = {
val r = regexList(l)
(s : String) => r.filter(t => matchDet(t._1, s)).map(_._2)
}