Controllare il modulo Control.Arrow.Transformer.Automaton nel pacchetto "frecce".Il tipo assomiglia a questo
newtype Automaton a b c = Automaton (a b (c, Automaton a b c))
Questo è un po 'di confusione perché è un trasformatore di frecce. Nel caso più semplice puoi scrivere
type Auto = Automaton (->)
Quali usi funziona come la freccia sottostante. Sostituendo (->) per "a" nella definizione Automa e l'utilizzo di notazione infissa potete vedere questo è più o meno equivalente a:
newtype Auto b c = Automaton (b -> (c, Auto b c))
In altre parole un automa è una funzione che prende un input e restituisce un risultato e un nuovo automa.
È possibile utilizzarlo direttamente scrivendo una funzione per ogni stato che accetta un argomento e restituisce il risultato e la funzione successiva. Ad esempio, ecco una macchina a stati per riconoscere la regexp "a + b" (cioè una serie di almeno una "a" seguita da una "b"). (Nota: codice non testato)
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)
In termini di domanda originale, Q = {stato1, Stato2}, X = Char, Delta è l'applicazione funzione e F è il passaggio dello stato di ritorno True (piuttosto che avere un "accepting state" Ho usato una transizione di output con un valore accettabile).
In alternativa è possibile utilizzare la notazione a freccia. Automaton è un'istanza di tutte le classi di frecce interessanti, tra cui Loop e Circuito, in modo da poter accedere ai valori precedenti utilizzando il ritardo. (Nota: nuovamente, codice non testato)
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 freccia "ritardo" che "prev" è uguale al valore precedente di "c" anziché il valore corrente. Puoi anche accedere al tuo output precedente usando "rec". Ad esempio, ecco una freccia che ti dà un totale in decomposizione nel tempo. (In realtà testata in questo 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
Si noti come l'ingresso di "ritardo" comprende "v", un valore derivato dalla sua uscita. La clausola "rec" abilita questo, quindi possiamo creare un ciclo di feedback.
Nel caso di un automa deterministico, Delta potrebbe essere del tipo Q -> X -> Q Nel caso di un automa non deterministico, avrei scelto qualcosa come Q -> X -> [Q] –
Cosa Sven Hager dice, e 'F' potrebbe essere implementato come' isEnd :: Q -> Bool' –