Consulte el módulo Control.Arrow.Transformer.Automaton en el paquete "arrows".El tipo se ve así
newtype Automaton a b c = Automaton (a b (c, Automaton a b c))
Esto es un poco confuso porque es un transformador de flecha. En el caso más simple, puede escribir
type Auto = Automaton (->)
Que usa funciones como la flecha subyacente. Sustituyendo (->) para "a" en la definición y Autómatas usando la notación infija se puede ver que esto es más o menos equivalente a:
newtype Auto b c = Automaton (b -> (c, Auto b c))
En otras palabras, un autómata es una función que toma una entrada y devuelve un resultado y un nuevo autómata
Puede usar esto directamente escribiendo una función para cada estado que toma un argumento y devuelve el resultado y la siguiente función. Por ejemplo, aquí hay una máquina de estados para reconocer la expresión regular "a + b" (es decir, una serie de al menos una 'a' seguida de una 'b'). (Nota: el código no probado)
state1, state2 :: Auto Char Bool
state1 c = if c == 'a' then (False, state2) else (False, state1)
state2 c = case c of
'a' -> (False, state2)
'b' -> (True, state1)
otherwise -> (False, state1)
En cuanto a su pregunta original, Q = {estado1, estado2}, X = Char, delta es la aplicación de funciones, y F es la transición de estado volviendo True (en lugar de tener una "estado de aceptación" He utilizado una transición de salida con un valor de aceptación).
También puede utilizar la notación de Flecha. Automaton es una instancia de todas las clases de flechas interesantes, incluidos Loop y Circuit, por lo que puede obtener acceso a los valores anteriores mediante el uso de la demora. (Nota: una vez más, el código no probado)
recognise :: Auto Char Bool
recognise = proc c -> do
prev <- delay 'x' -< c -- Doesn't matter what 'x' is, as long as its not 'a'.
returnA -< (prev == 'a' && c == 'b')
La flecha "retraso" significa que "prev" es igual al valor anterior de "c" en lugar del valor actual. También puede obtener acceso a su salida anterior mediante el uso de "rec". Por ejemplo, aquí hay una flecha que le da un total en descomposición a lo largo del tiempo. (En realidad probado en este caso)
-- | Inputs are accumulated, but decay over time. Input is a (time, value) pair.
-- Output is a pair consisting
-- of the previous output decayed, and the current output.
decay :: (ArrowCircuit a) => NominalDiffTime -> a (UTCTime, Double) (Double, Double)
decay tau = proc (t2,v2) -> do
rec
(t1, v1) <- delay (t0, 0) -< (t2, v)
let
dt = fromRational $ toRational $ diffUTCTime t2 t1
v1a = v1 * exp (negate dt/tau1)
v = v1a + v2
returnA -< (v1a, v)
where
t0 = UTCTime (ModifiedJulianDay 0) (secondsToDiffTime 0)
tau1 = fromRational $ toRational tau
Nota cómo la entrada a "retraso" incluye "v", un valor derivado de su salida. La cláusula "rec" lo habilita para que podamos crear un ciclo de retroalimentación.
En caso de un autómata determinista, delta podría ser del tipo Q -> X -> Q En caso de un autómata no determinista, elegiría algo así como Q -> X -> [Q] –
Lo Sven Hager dice, y 'F' podría implementarse como' isEnd :: Q -> Bool' –