Haskell 在无国籍的世界中维持国家

Haskell 在无国籍的世界中维持国家,haskell,state,monads,context-free-grammar,Haskell,State,Monads,Context Free Grammar,我正在将上下文无关语法转换为Greibach范式(GNF)。主要的转换(来自Hopcroft&Ullman)是语法索引变量上的一系列迭代。它本质上是“无国籍的”。我将其实现为适当索引上的一系列折叠(实现相当简单): maxIndex rl返回一组规则中的最大变量索引;subst rl k j通过其右侧以j索引变量开头的规则对k索引规则执行替换。在执行gnf之后,我需要以相反的顺序对语法进行最后一次检查 问题是noLR,它使用左递归k索引规则转换语法。这是一个“有状态”函数,因为必须为应用noLR

我正在将上下文无关语法转换为Greibach范式(GNF)。主要的转换(来自Hopcroft&Ullman)是语法索引变量上的一系列迭代。它本质上是“无国籍的”。我将其实现为适当索引上的一系列折叠(实现相当简单):

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::Ord a=>Int->Set(规则a)->State[Sym a](规则a))

noLR rl=do(n:ns)我会尝试将noLR重写为纯的。您确定不能重写它以生成仅依赖于规则名称及其索引(或类似内容)的符号吗


为了将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变得纯粹-类似于k==42和j==16的“foo_42_16”。但是,如果输入语法已经包含此类符号名,您可能会遇到麻烦

如果你需要你的符号在语法中是唯一的,那么为什么不这么说呢

newSymbol :: Set (Rule a) -> Sym a
newSymbol rl = ... find a symbol name not used in rl ...

但是,这肯定是没有效率的,除非您将Set(规则a)替换为另一种类型,以便更有效地实现newSymbol操作。

请参见
foldM
gnf
当然必须以顶级evalState调用开始。因为折叠是累积规则集,所以我可以重写noLR以重新计算当前规则集中的下一个可能变量。由于这种设置方式,查找下一个变量是对规则集的一个相当简单的折叠操作。不过,作为一个学习练习,我经常遇到这个问题(我需要的唯一有状态计算是嵌入“递归的深处”)。我想知道有人会如何用一个人的方式来表达它。第二个建议很好!将集合与表示其中使用的max符号的int配对。从某种意义上说,这只是使monad显式化,但在这种情况下,它感觉更优雅。在这种情况下,重新评估下一个变量可能是最好的解决方案。它使代码总体上更干净。但是
foldM
的例子为我澄清了它的用法:它通过左折叠将
s
a
值传递到
a
上。当遇到“有状态”计算时,将执行该计算并更新
s
。很酷,谢谢!
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)
newSymbol :: Set (Rule a) -> Sym a
newSymbol rl = ... find a symbol name not used in rl ...