【发布时间】:2010-11-23 19:09:53
【问题描述】:
我正在将上下文无关语法转换为 Greibach 范式 (GNF)。主要的转换(来自 Hopcroft & Ullman)是对语法的索引变量的一系列迭代。它本质上是“无状态的”。我已经将它实现为适当索引上的一系列折叠(实现相当简单):
gnf :: Ord a => Set (Rule a) -> Set (Rule a)
gnf rl = foldl step1 rl [1..maxIndex rl]
where step1 rl' k = foldl step2 rl' [1..k - 1]
where step2 rl'' j = noLR k (subst rl'' k j)
maxIndex rl 返回一组规则中的最大变量索引; subst rl k j 用右手边以 j 索引变量开始的规则对 k 索引的规则执行替换。执行完gnf后,我需要以相反的顺序对语法进行最后一次遍历。
问题是noLR,它用左递归k索引规则转换语法。这是一个“有状态”函数,因为必须为每个应用 noLR 的规则(或 k 索引规则)生成一个唯一变量。所以我写了一个有状态的函数
noLR :: Ord a => Int -> Set (Rule a) -> State [Sym a] (Set (Rule a))
noLR rl = do (n:ns) <- get; put ns;
let rl' = ... remove left recursion rl n ...
in return rl'
我可以对 noLR 进行排序,以更新 noLR 将其作为参数的 n。不过,我不确定如何在上述函数的 step2 内执行 noLR。我似乎无法使用 let ... in 架构,因为有状态计算嵌入在几个递归函数中。
我想做的是让 n 成为某种类型的全局变量,类似于 n 的显式线程,我可以在 内部调用和更新它step2,这就是为什么我最初使用 eta-expansion(对于 n)将函数编写为折叠的原因。有谁知道我如何在状态单子中构造 gnf 来实现这种效果?除了折叠中的最后一个计算,没有其他东西是“有状态的”,我只喜欢使用带有“琐碎”示例的状态单子。我有点迷路了。
【问题讨论】:
-
见
foldM。gnf当然会从顶级 evalState 调用开始。 -
也可以看看 Luke Palmer 的IO-free splittable supply。
标签: haskell state monads context-free-grammar