查看“箭头”包中的 Control.Arrow.Transformer.Automaton 模块。类型是这样的
newtype Automaton a b c = Automaton (a b (c, Automaton a b c))
这有点令人困惑,因为它是一个箭头转换器。在最简单的情况下,您可以编写
type Auto = Automaton (->)
它使用函数作为底层箭头。将 Automaton 定义中的“a”替换为 (->) 并使用中缀表示法,您可以看到这大致相当于:
newtype Auto b c = Automaton (b -> (c, Auto b c))
换句话说,自动机是一个接受输入并返回结果和新自动机的函数。
您可以通过为每个状态编写一个函数来直接使用它,该函数接受一个参数并返回结果和下一个函数。例如,这里有一个状态机来识别正则表达式“a+b”(即一系列至少一个“a”后跟一个“b”)。 (注:未经测试的代码)
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)
就您的原始问题而言,Q = {state1, state2}, X = Char, delta 是函数应用程序,F 是返回 True 的状态转换(而不是“接受状态”,我使用了输出接受值的转换)。
您也可以使用箭头符号。 Automaton 是所有有趣的箭头类的一个实例,包括 Loop 和 Circuit,因此您可以使用延迟来访问以前的值。 (注意:再次,未经测试的代码)
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')
“delay”箭头表示“prev”等于“c”的前一个值,而不是当前值。您还可以使用“rec”访问之前的输出。例如,这里有一个箭头,它会随着时间的推移为您提供一个衰减的总数。 (本例实际测试过)
-- | 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
注意“延迟”的输入如何包含“v”,一个从其输出派生的值。 “rec”子句可以实现这一点,因此我们可以建立一个反馈循环。