Fixed-point combinators为匿名函数提供了一种引用自身或构建相互递归结构的方法。尽管在lambda演算中很有用,但它们在现代编程语言中基本上是多余的,因为大多数(如果不是全部)都支持递归,lambdas和闭包。
此外,定点组合器可以使递归构造像左递归语法分析器终止。考虑Lickman 1995,他证明了他的实现已经终止,但从未真正提到 它是如何工作的(它只是从格理论到haskell实现的逐步推导)和为什么他需要一种已经支持递归的语言的定点组合器。
它是如何工作的,为什么他需要一个定点组合器?
答案 0 :(得分:4)
从快速扫描开始,在5.3结束时,Lickman写道"如上所述,确保fixS在所有连续输入上都足够高效。"
重点是让fixpoint操作符产生足够的输出,以便解析可以继续。你不能为一般fix :: (a -> a) -> a
执行此操作,但专门设置a
到Set a
或更晚Parser a
,可以提供足够的结构(即格子的结构)来工作用。
同样,我只是轻轻地浏览了论文,但我认为声明(见第5.5节)的声明" h :: Parser a -> Parser a
保持了生产力的属性==> fixP h
是富有成效的"是关键。
答案 1 :(得分:3)
当然可以。这是一个简单的右递归语法,有三个规则:
S -> b T
T -> a S
T -> a
这三个规则让我们构建了一个用于识别这些字符串的解析器:
type Parser = String -> (Bool, String)
s :: Parser
s "" = (False, "")
s (c : cs) = if c == 'b' then t cs else (False, cs)
t :: Parser
t "" = (False, "")
t (c : cs)
| c == 'a' && cs == "" = (True, "")
| c /= 'a' = (False, cs)
| otherwise = s cs
如果您想进行更一般的解析,只需将Bool
专门化为具有一些数据结构,可能存储在Maybe
中以指示失败。如果S还有其他规则,例如,例如,在失败的解析上返回(False, ___)
会有所帮助。 S -> T T
和T -> b b
。然后,当我们得到一个'b'后跟(False,___)时,我们倒回尝试S -> T T
。这些语法可以用一点肘部油脂和递归来完成。
上述三条规则将成功匹配“ba”,“baba”等字符串。我们也可以将这些字符串左递归写为:
S -> T a
T -> S b
T -> b
如果您尝试在上面编写相同的解析器会发生什么?无限循环,如果您正在查看字符串的前面。问题是函数S将首先调用函数T,然后T将首先调用S,它们将无限地相互递归。计算机不够聪明,不知道后置条件(“后跟一个”,“后跟一个b”)使得进一步的解决方案变得不可能;它只是落入你的职能部门并相信你知道自己在做什么。
一个好的定点组合器如何帮助?好吧,将这些规则视为描述树:然后函数评估遍历树深度优先,并且此特定树在该方向上是无限的。另一方面,广度优先遍历可以基于这些规则并且可以获得使用这些函数中最少的函数的结果,并且这是基于该语法的特定函数的“最不固定点”。这就是为什么正确的定点组合子(基于论文中的diag
或格子理论组合子)在描述这些规则时可以终止,而朴素递归则不会。 / p>