【问题标题】:Keeping State in a Stateless world在无状态世界中保持状态
【发布时间】: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 来实现这种效果?除了折叠中的最后一个计算,没有其他东西是“有状态的”,我只喜欢使用带有“琐碎”示例的状态单子。我有点迷路了。

【问题讨论】:

标签: haskell state monads context-free-grammar


【解决方案1】:

为了使用你给定的类型的 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 = 的“foo_42_16” = 42 和 j == 16。如果输入语法已经包含这种符号名称,那么您可能会遇到麻烦。

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

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

但是,这绝对不是有效的,除非您将 Set (Rule a) 替换为不同的类型,以便更有效地实现 newSymbol 操作。

【讨论】:

  • 第二个建议不错!将集合与表示其中使用的最大符号的 int 配对。从某种意义上说,这只是使 monad 变得明确,但在这种情况下,它感觉更加优雅。
  • 在这种情况下,重新评估下一个变量可能是最好的解决方案。它使代码整体更清晰。但是foldM 的示例为我清除了它的用途:它通过左折叠将s 和a 值传递给a。当遇到“有状态”计算时,会执行并更新s。很酷,谢谢!
【解决方案2】:

我会尝试将 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

【讨论】:

  • 因为折叠正在累积规则集,我可以重写 noLR 以从当前规则集中重新评估下一个可能的变量。由于这种设置方式,查找下一个变量是对规则集的相当简单的折叠操作。不过,作为一项学习练习,我经常遇到这个问题(我需要的唯一有状态计算是嵌入“递归深处”)。我想知道有人会如何以一元的方式表达它。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-01-19
  • 1970-01-01
  • 2016-04-10
  • 2019-08-01
  • 2012-03-09
相关资源
最近更新 更多