Java 实现统一和跳过变量
我正在以通常的方式实现通常的统一算法:通过表达式树递归下降,沿途向哈希表添加变量绑定,执行发生检查。在Java中,使用覆盖函数符合语言的粒度,因此实现中处理变量的部分是:Java 实现统一和跳过变量,java,algorithm,performance,unification,Java,Algorithm,Performance,Unification,我正在以通常的方式实现通常的统一算法:通过表达式树递归下降,沿途向哈希表添加变量绑定,执行发生检查。在Java中,使用覆盖函数符合语言的粒度,因此实现中处理变量的部分是: @Override public boolean unify(Term a, Map<Var, Term> map) { if (this == a) { return true; } Term x = map.get(this); if (x != null) {
@Override
public boolean unify(Term a, Map<Var, Term> map) {
if (this == a) {
return true;
}
Term x = map.get(this);
if (x != null) {
return x.unify(a, map);
}
if (a instanceof Var) {
x = map.get((Var) a);
if (x != null) {
return x.unify(this, map);
}
}
if (a.occurs(this)) {
return false;
}
map.put(this, a);
return true;
}
然后每次一个新变量必须统一到同一事物上,它必须一步一步地通过链找到它现在的位置,这需要O(N)个时间,这意味着将一组变量统一到同一事物上需要总时间O(N^2)
也许最好的解决方案是实现某种快捷方式,即沿着更新a
的路线直接指向当前的最终目标,不管是什么。如何以一种在所有情况下都是正确和有效的方式做到这一点并不十分明显
统一已经广为人知并广泛使用了几十年,所以我想解决这个问题的方法肯定也已经知道几十年了,但我看到的关于统一的几次讨论似乎都没有提到它
修改算法以处理它的具体方法是什么?我同意快捷方式是正确的方法。您应该能够更改以下内容:
return x.unify(a, map);
为此:
if (! x.unify(a, map)) {
return false;
}
map.put(this, map.get(x));
return true;
if (! x.unify(this, map)) {
return false;
}
map.put(a, map.get(x));
return true;
这是:
return x.unify(this, map);
为此:
if (! x.unify(a, map)) {
return false;
}
map.put(this, map.get(x));
return true;
if (! x.unify(this, map)) {
return false;
}
map.put(a, map.get(x));
return true;
(每个单独的map.put
只切除一个级别的间接寻址,但由于您在递归调用之后立即进行该操作,该调用也会切除任何不必要的间接寻址,因此您知道只有一个级别的间接寻址可供切除。)
这并不能完全防止连锁,因为可以将
a
与b
统一,然后将b
与c
统一,依此类推;但是每个链在第一次遇到它时都会被完全处理,所以你仍然会得到固定时间的摊销。这里有一个想法:所有通过=
连接的变量都是一个等价类。所以你可以做地图
unify(Term a, Map<VarClass, Term> map) {...
统一(术语a,地图){。。。
其中,VarClass
通过实现
当您发现以前添加到映射中的变量对x=y
时,将x
添加到包含y
的VarClass
中(创建一个变量对,如果不存在,则使用可变的空占位符映射将其添加)
地图右侧的术语
永远不是Var
union find操作在所有实际用途中都是固定时间摊销的,在实践中非常快。我不知道(乍一看),但问题很好!我认为这基本上是正确的答案!需要注意的是,每个map.put调用之前都需要进行检查,以确保map.get不为null。我认为这是因为递归调用可能只是将一个变量与自身统一,不留新的绑定。此外,它还通过了我的单元测试并运行快得多。