Skip to main content
added 260 characters in body
Source Link
fededevi
  • 330
  • 2
  • 9
  • Representation of the expression in memory: The Expression class hierarchy
  • Building the expressions from strings: The parser for infix notation
  • Evaluating the expressions: evaluate() method on Expression interface (or ExpressionEvaluator visitor)
  • Printing the expressions: toString() method on Expression interface (or ExpressionWriter visitor)

Get rid of MathResult and Operands. Everything can be an Expression (Operation).

You can move the evaluate and toString code to separate visitors if you want to separate the representations from the logic. This can be useful, for example, if you want to be able to print your expression with different notations you can have different visitors that share a lot of code: ExpressionPrefixWriter, ExpressionInfixWriter, ExpressionPostfixWriter.

  • Representation of the expression in memory: The Expression class hierarchy
  • Building the expressions from strings: The parser for infix notation
  • Evaluating the expressions: evaluate() method on Expression interface (or visitor)
  • Printing the expressions: toString() method on Expression interface (or visitor)

Get rid of MathResult and Operands. Everything can be an Expression (Operation).

You can move the evaluate and toString code to separate visitors if you want to separate the representations from the logic.

  • Representation of the expression in memory: The Expression class hierarchy
  • Building the expressions from strings: The parser for infix notation
  • Evaluating the expressions: evaluate() method on Expression interface (or ExpressionEvaluator visitor)
  • Printing the expressions: toString() method on Expression interface (or ExpressionWriter visitor)

Get rid of MathResult and Operands. Everything can be an Expression.

You can move the evaluate and toString code to separate visitors if you want to separate the representations from the logic. This can be useful, for example, if you want to be able to print your expression with different notations you can have different visitors that share a lot of code: ExpressionPrefixWriter, ExpressionInfixWriter, ExpressionPostfixWriter.

Source Link
fededevi
  • 330
  • 2
  • 9

I want to add to the other answers and provide a different more drastic suggestion and a more generic and (I think) clean approach.

This is the perfect example where a nice class hierarchy shines.

This is also the perfect example where to use visitors.

I think in this case everything should be an expression.

First, the problem can be broken down in separate blocks:

  • Representation of the expression in memory: The Expression class hierarchy
  • Building the expressions from strings: The parser for infix notation
  • Evaluating the expressions: evaluate() method on Expression interface (or visitor)
  • Printing the expressions: toString() method on Expression interface (or visitor)

Get rid of MathResult and Operands. Everything can be an Expression (Operation).

interface Expression {
    Expression evaluate();
    String toString();
}

MathResult should be a Literal->Expression just like inputs, a Literal is just a number. You can also define different literals for different types if you want to be more generic (BooleanLiteral, Stringliteral, etc..)

class Int->Literal->Expression {
    int value;
    Expression evaluate() {return this};
    String toString() {
        return intToString(value);
    }
}

Operation is an expression, you will have to add classes for each operation:

abstract class BinaryExpression {
    Expression left;
    Expression right;
}

public class Addition->BinaryExpression {
     int value;
     Expression evaluate() {
          Expression r = right.evaluate();
          Expression l = left.evaluate()
          if (l and r are Int)
              return new Int( l.value() + r.value())
          else
              return new Addition(l, r);
    };

    String toString() {
        return left.toString() + " + " + right.toString();
    }
}

The Parser

The parser will turn strings into expression hierarchy, e.g:

"1 + 2" -> new Add(new Int(1), new Int(2))

A more complex parser might be able to read and create more complex expressions, e.g:

"1 * (2 + 4)" -> new Mul(Int(1),Add(Int(2),Int(4)))

There are software that helps you create parsers e.g.: javaCC

And then to use it:

Expression e = ExpressionInfixParser.parse("2 + 4");
print(e.toString()) //prints "2 + 4"
print(e.evaluate().toString()) //prints "6"

As you can see the class hierarchy is very generic and can represent many different expressions as long as you implement the operations you are interested in and the parser can read them. The interfaces are also very simple.

You can move the evaluate and toString code to separate visitors if you want to separate the representations from the logic.

You can also easily extend the functionality with additional classes, e.g. you can have a Variable class that represent generic variables:

class Variable->Expression {
    String name;
    String toString() {
        return name;
    }
    Expression evaluate( Context context ) {
        if (context.contains(name))
            return context[name];
        else
            return this;
    }
}

And then evaluate expression in different contexts, a context can be as simple as:

class Context -> Map<String, Expression> {}

Example:

Expression e = ExpressionInfixParser.parse("a * ( 2 * 2 ) + b");

print(e.toString()) //prints "a * ( 2 * 2 ) + b"

print(e.evaluate().toString()) //prints "a * 4 + b"

Context c = {{"a", new Int(5)}}
print(e.evaluate(c).toString()) //prints "20 + b"

Context c = {{"a", Int(2)}, {"b", Int(9)}}
print(e.evaluate(c).toString()) //prints "17"

Sorry for the code syntax, it is more of a pseudocode.