【发布时间】:2022-01-24 19:53:43
【问题描述】:
为了举例,让我们定义一个玩具自动机类型:
data Automaton =
Auto
{ success ::
Automaton
, failure ::
Automaton
}
这个结构是按循环设计的,我们可以把每一个Automaton想象成一个状态,成功和失败转换到其他状态。所以有限自动机必须递归定义。例如这里是最简单的自动机:
sink =
Auto sink sink
它由 1 个状态组成,该状态始终转换为自身。如果我们愿意,我们可以制作更复杂的自动机:
-- Transitions to a sink once it encounters a failure
otto1 =
Auto otto1 sink
-- Mutually recursive automata
otto2 =
Auto otto2 otto3
otto3 =
Auto otto3 otto2
这些很好。但是接受用户输入并构建一个自动机可能会很好。例如,可以从转换矩阵中构建一个。这是一个简单的实现:
fromTransition :: [(Int, Int)] -> Automaton
fromTransition tMatrix =
go 0
where
go n =
let
(succ, fail) =
tMatrix !! n
in
Auto (go succ) (go fail)
但是,当我们尝试这样做时,就会出现问题。我们之前的例子是 O(1) 跟随转换。然而,由此产生的自动机是O(n) 跟随转换,因为除非缓存,每次我们进行转换时都必须索引一个列表。此外,只要这个自动机存在,输入列表就必须保存在内存中。这基本上比使用转移矩阵作为自动机更糟糕。
我真正想要的是使用该方法动态构建的自动机与之前所示的静态构建的自动机一样高效。我想要一些方法来分析输入,构造一个自动机,然后释放输入。
在具有变异的语言中,这很容易做到,因为我们可以一点一点地创建结构,留下漏洞以便以后更正。
我也非常希望不要将IO 拖进来,因为一旦引入它就无法包含。
有没有像我想要的那样动态分配循环结构的好方法?
【问题讨论】:
-
迂腐提示:照原样,您的
Automaton类型不是很有用,因为无法区分一个值与另一个值。所以,fromTransition _ = let a = Auto a a in a技术上是一个正确的答案。当然,在Automaton中添加一些字段,比如Int,让问题不再微不足道。
标签: haskell memory-management recursive-datastructures