我有一个项目,需要将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处停止。

10-06 13:41