我正在将无上下文语法转换为Greibach Normal Form(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 。不过,我不确定如何在上面的函数中执行 noLR step2 。我似乎无法使用 let ... in 模式,因为有状态计算嵌入在几个递归函数中。
我想要做的是让 n 成为某种类型的全局变量,类似于 n 的显式线程,我可以在中调用和更新step2 ,这就是为什么我最初用 eta -expansion(对于 n )将函数编写为折叠的原因。有谁知道如何在状态monad中构造 gnf 来实现这种效果?除了折叠中的最后一次计算外,没有别的东西是“有状态的”,而我只是习惯使用状态monad和“琐碎”的例子。我很失落。
答案 0 :(得分:4)
为了使用noLR和你给出的类型,你必须按照以下几行重写你的gnf函数:
gnf :: Ord a => Set (Rule a) -> Set (Rule a)
gnf rl = evalState (foldM step1 rl [1..maxIndex rl]) ( ... generate the initial state [Sym a] here ...)
where step1 rl' k = foldM step2 rl' [1..k - 1]
where step2 rl'' j = noLR k (subst rl'' k j)
您的状态变量在整个计算过程中都存在,并且必须在代码中明确该事实。
如果您需要的是新生成的变量名称不会相互冲突,那么您可以通过从索引k和j生成新的符号名称来使noLR纯净 - 类似于“foo_42_16”的k = = 42和j == 16.如果输入语法已包含该类型的符号名称,则可能会遇到麻烦。
如果您需要符号在语法中是唯一的,那么为什么不这么说?
newSymbol :: Set (Rule a) -> Sym a
newSymbol rl = ... find a symbol name not used in rl ...
这绝对不是有效的,除非你用一个不同的类型替换Set(规则a),它允许你更有效地实现newSymbol操作。
答案 1 :(得分:3)
我会尝试将noLR重写为纯粹的。您确定无法重写它以生成仅依赖于规则名称及其索引(或类似内容)的符号吗?
noLR k j = noLR' k j $ newSymbol k j
where newSymbol k j = ... -- some concatenation of k and j
noLR' k j sym = ... -- your now pure function