我正在研究编译器/校对检查器,我想知道,如果我有这样的语法树,例如:
data Expr
= Lambdas (Set String) Expr
| Var String
| ...
如果有办法检查Expr
s的alpha等价(等价模重命名)。然而,这个Expr
与lambda演算的不同之处在于lambda中的变量集是可交换的 - 即参数的顺序不会影响检查。
(但为了简单起见,Lambda ["x","y"] ...
与Lambda ["x"] (Lambda ["y"] ...)
不同,在这种情况下,顺序确实很重要。
换句话说,给定两个Exprs
,如何才能有效地找到从一个到另一个的重命名?这种组合问题的气味是NP-complete。
答案 0 :(得分:6)
参数的可交换性暗示了指数比较,是真的。
但我怀疑你可以规范化参数列表,所以你只需要按单个顺序比较它们。然后,与重命名进行比较的树在树的大小上基本上是线性的。
我建议做的是,对于每个参数列表,访问子树(按顺序,后序,只要您保持一致无关紧要)并按参数顺序的索引对参数进行排序首先遇到参数使用。所以,如果你有
lambda(a,b): .... b ..... a ... b ....
您将参数列表排序为:
lambda(b,a)
因为你首先遇到b,然后是第二次,而b的额外遭遇并不重要。将树与规范化参数列表进行比较。
如果你坚持lambda子句中的运算符可以是可交换的,那么生活会变得更加混乱。我的猜测是你仍然可以将它标准化。
答案 1 :(得分:4)
我们可以向Daan Leijen的HMF提出一些想法。 (他处理的是& foralls'的粘合剂,也是可交换的。
特别是,他以正文中的发生顺序重新绑定变量。
然后比较术语涉及同样的方式和比较结果。
我们可以通过用locally nameless representation替换那个skolemization传递来做得更好。
data Bound t a = Bound {-# UNPACK #-} !Int t | Unbound a
instance Functor (Bound t) where ...
instance Bifunctor Bound where ...
data Expr a
= Lambdas {-# UNPACK #-} !Int (Expr (Bound () a))
| Var a
所以现在在lambda下出现Bound是由lambda直接绑定的变量,以及你想要放在出现的任何类型信息,这里我只是用()。
现在封闭的术语在' a'中是多态的。并且,如果您通过其使用站点对lambda的元素进行排序(并且可以确保您通过删除未使用的术语来始终规范化lambda),则alpha等效术语仅与(==)进行比较,如果您需要打开术语,则可以使用Expr String或其他一些表示。
Expr和Bound的签名的更肛门保留版本将使用存在类型和类型级别自然来识别被绑定的变量的数量,并使用' Fin'在绑定构造函数中,但是由于您必须维护不变量,因此不会绑定比lambda中出现的#更多的变量,并且类型信息在具有相同Var (Bound n _)
的所有n
中都是一致的,维持另一个人并没有太大的负担。
更新:您可以使用我的bound
软件包以完全独立的方式对其进行改进!