我正在将上下文无关语法转换为格雷巴赫范式 (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 kj用右手边以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 ...,因为有状态计算嵌入在几个递归函数中。
我想要做的是让n成为某种类型的全局变量,类似于n的显式线程,我可以在step2中调用和更新它,这就是为什么我最初将函数编写为带有eta -expansion 的折叠(对于n)。有谁知道我如何在 state monad 中构造gnf来实现这种效果?除了折叠中的最后一次计算,没有其他东西是“有状态的”,我只喜欢使用带有“琐碎”示例的状态单子。我比较迷茫。