我有一个项目,需要将x的值转换为1 + 5并将其计算为6。
如果x是唯一需要转换的值,我没有问题,但是如果我有(5 * x),它将成为一个问题。它没有尝试执行5 *(1 + 5)并将其计算为30,而是简单地尝试计算5 * x,这导致了一个异常,即由于存在变量而无法进行计算。
ExpFoo类是一个接口,它是TimesExpFoo,PlusFoo,IntFoo和VarFoo的超类。替换类完全分开。
TimesExpFoo将数字相乘,而PlusFoo将数字相加。
这是主班。这将获得varfoo和数字。 Replacement类将varfoo和数字放在由Replacement类使用的哈希图中。 varfoo是键,数字是值。
ExpFoo x = new VarFoo("x");
ExpFoo e1 = new IntFoo(1);
ExpFoo e2 = new IntFoo(2);
ExpFoo e5 = new IntFoo(5);
ExpFoo times = new TimesExpFoo(e5, x);
Replacement s = new Replacement();
s.put(new VarFoo("x"), new PlusExpFoo(e1, e5));
times.computeValue(s);
System.out.println(times.computeValue(s));
然后将使用computeValue方法进入ExpFoo类,该子项包含[x:= 1 + 5],它将首先检查applyReplacement方法:
default int computeValue(Replacement subst) {
ExpFoo specialised = applyReplacement(subst);
return specialised.computeValue();
}
然后,它将使用applyReplacement方法转到TimesExpFoo类,它将返回[x:= 1 + 5]:
@Override
public ExpFoo applyReplacement(Replacement s) {
return this;
}
它将返回到ExpFoo类,这一次专门有(5 * x)而subst有[x:= 1 + 5],不幸的是它将返回专门值(5 * x):
default int computeValue(Replacement subst) {
ExpFoo specialised = applyReplacement(subst);
return specialised.computeValue();
}
然后将转到TimesExpFoo的computeValue方法,getLeft方法包含5,getRight方法包含x:
@Override
public int computeValue() {
return getLeft().computeValue() * getRight().computeValue();
}
最终将转到VarFoo类并使用computeValue方法,该方法将引发由于存在变量而无法计算的错误。
我知道TimesExpFoo的applyReplacement方法返回了subst本身的值,我可以做更多的事情,但是我不确定它是如何工作的。我已经尝试过使用
return s.get((VarFoo) getRight())
,但是它只会给我一个铸造错误TimesExpFoo cannot be cast to class VarFoo
。我的VarFoo类重写equals和hashcodes方法。如果varfoo x具有相同的哈希码,则应使用5 *(1 + 5)覆盖5 * x。自己做是没有问题的。
我不会为x变量覆盖会感到困惑。它与哈希图有关吗?
我的applyReplacement方法只是Expression类中的一个签名,因此我怀疑这就是问题所在。
这是使用哈希图的Replacement类:
public class Replacement {
private Map<VarFoo, ExpFoo> replacementMap;
public Replacement() {
replacementMap = new HashMap<>();
}
public ExpFoo put(VarFoo var, ExpFoo exp) {
if(replacementMap.containsKey(null) || replacementMap.containsValue(null)){
throw new NullPointerException();
}
return replacementMap.put(var, exp);
}
public boolean forget(VarFoo var) {
if(replacementMap.containsKey(null)) {
throw new NullPointerException();
}
else {
if(!replacementMap.containsKey(var))
return true;
}
return false;
}
public ExpFoo get(VarFoo var) {
if(replacementMap.containsKey(null)){
throw new NullPointerException();
}
return replacementMap.get(var);
}
public boolean hasMappingFor(VarFoo var) {
if(replacementMap.containsKey(var)){
return true;
}
else if(replacementMap.containsKey(null)){
throw new NullPointerException();
}
return false;
}
}
我的VarFoo的equals和hashcode方法,它使用实例变量
name
,它是一个String: @Override
public boolean equals(Object o) {
if (o == null) return false;
if (!(o instanceof VarFoo))
return false;
if (o == this)
return true;
return name.equals(((VarFoo) o).name);
}
@Override
public int hashCode() {
int prime = 31;
int result = 1;
result = prime * result + ((name == null) ? 0 : name.hashCode());
return result;
}
TimesExpFoo类是BinaryExpFoo的子类,它是一个抽象类,它是ExpFoo的子类。 BinaryExpFoo是提供left,right和operatorSymbol实例变量的变量。
public class TimesExpFoo extends BinaryExpFoo {
public TimesExpFoo(ExpFoo left, ExpFoo right) {
super(left, right, "*");
}
@Override
public int computeValue() {
return getLeft().computeValue() * getRight().computeValue();
}
@Override
public ExpFoo applyReplacement(Replacement s) {
return this;
}
@Override
public boolean equals(Object o) {
if (!(o instanceof TimesExpFoo)) {
return false;
}
return super.equals(o);
}
@Override
public int hashCode() {
return super.hashCode();
}
最佳答案
applyReplacement()
方法的目标是将所有变量替换为其实际值/表达式。唯一可以做到这一点的表达式显然是VarFoo
类。其方法可能如下所示:
@Override
public ExpFoo applyReplacement(Replacement s) {
ExpFoo exp = s.get(this);
exp = exp.applyReplacement(s); // replace any contained variables as well
return exp;
}
与
computeValue()
方法类似,对于包含其他applyReplacement()
对象的类,必须递归实现ExpFoo
方法。这些“内部”表达式也必须进行“转换”,以替换变量。例如,对于TimesExpFoo
类,该方法可能如下所示:@Override
public ExpFoo applyReplacement(Replacement s) {
return new TimesExpFoo(
this.left.applyReplacement(s),
this.right.applyReplacement(s)
);
}
这样,
TimesExpFoo
对象将转换为相似的TimesExpFoo
对象,而不再包含任何变量。然后,由于所有内容都已替换,因此可以使用computeValue()
方法。您的实际实现必须检查链式变量替换。假设您有以下表达式:
x = 5
y = 4+x
z = 3+y
您可能需要某种循环或递归调用(如上所示),因此表达式
z
最终将转换为3+4+5
,而不仅仅是在3+y
处停止。