【发布时间】:2014-08-08 14:43:39
【问题描述】:
大家好,我是 Android 世界的新手,我正在尝试构建一个中等难度的计算器应用程序。我想出了这种结构,但还没有实现任何东西,因为我有点怀疑这是否是正确的方法。所以我来了:
我有一个 Operand 接口,它带有一种称为“getValue()”的方法,它应该返回一个双精度类型,以及一些实现该接口的其他对象,从而实现该“getValue()”方法:
-
Expression,我考虑过让它成为一个具有私有成员字段的对象,该字段将存储所有操作,所以这个 Expression Object 会有这样的东西:
public class Expression implements Operand { private List<Operation> operationList; ... }operationList 将是一个 ArrayList,其中 Operation 是另一个对象,它涉及两个操作数(因此两个操作数接口作为类型)与一个 Operator 对象绑定在一起,该对象具有一个枚举类型,表示涉及这两个操作数的操作是否为SUM、DIV、MUL 或 SUB。比如:
public class Operation { private Operand operand1; private Operand operand2; private Operator operator; public Operation(Operand operand1, Operator operator, Operand operand2) { this.operand1 = operand1; this.operand2 = operand2; this.operator = operator; } public double getResult() { if (operator.getType() == Operator.Type.SUM) { return operand1.getValue() + operand2.getValue(); } else if (operator.getType() == Operator.Type.SUB) { return operand1.getValue() - operand2.getValue(); } else if (operator.getType() == Operator.Type.MUL) { return operand1.getValue() * operand2.getValue(); } else if (operator.getType() == Operator.Type.DIV) { return operand1.getValue() / operand2.getValue(); } return 0; } ... }
通过这种方式,即使在像“3 + (4 * 3 - 2 * (4 - 1) / 2 + 5))”这样的表达式中有子表达式,由于接口的原因,它也会像操作数一样被计算,在特别是在这种情况下:
operator All this is another Expression object (a sub-expression that is treated
| | like an Operand
| ________|____________ cause it implements the Operand
3 + (4 * 3 - 2 * (4 - 1)) interface).
|
Operand operand1
实现 Operand 的数字类,它将是一个用于双精度值的简单包装器类,并将在其实现的 getValue() 方法中返回该双精度值。
具有枚举类型的函数类,表示它是 SIN、COS、TAN、ARCSIN、ARCCOS、ARCTAN、LOG 还是 LN 函数,始终使用 getValue() 方法返回计算的双精度结果功能;
实用程序,这个类可能会被误解:它是用于诸如求幂、平方根、数字的百分比或其阶乘之类的操作的类。这里的想法是再次使用枚举类型来区分操作的类型。我知道取幂本身就是一个运算,但对我来说最好让它远离前面解释的 Operation 类,因为结构和计算是不同的(我想将 Exponentiation 视为 Operand 类型,而不是 Operation ,对我来说,一个操作只涉及两个操作数和一个操作符,正如我之前所说的)。
然后我知道,给定这样的表达式:
3 + 4 * ((5 + 2) - √4 + sin(4) + 3²) / 2
数据结构是:
Operand Operand
| |
3 + 4 * ((5 + 2) - √4 + sin(4) + 3²) / 2
| ¯¯¯¯¯¯¯¯¯¯¯¯¯|¯¯¯¯¯¯¯¯¯¯¯¯¯¯
Operand Operand
4 个操作数,因此 3 个操作,但我需要建立优先级,所以我认为我应该遍历 operationList(Expression 对象中的私有字段)以获得这样的优先级,因为我的数据结构就像(基于此表达式,伪代码):
列表:
- Item n° 1 -> Operation(Number(3), Operator(Operator.Type.SUM), Number(4));
- Item n° 2 -> Operation(Number(4), Operator(Operator.Type.MUL), Expression("(5 + 2) - √4 + sin(4) + 3²"));
- Item n° 3 -> Operation(Expression("(5 + 2) - √4 + sin(4) + 3²"), Operator(Operator.Type.DIV), Number(2));
遍历这个列表我可以发现操作编号 2(项目编号 2)必须在操作编号 1 之前执行。但我认为这种方式不太好,因为您看到我需要整理所有 ArrayList每次我在执行操作 N°2 并获得双重结果时从操作原因中获得结果时,我需要为该结果创建一个 Number() 对象,以再次将其视为操作数并以某种方式重新组织 ArrayList第一个操作不再将 Number(4) 作为第二个操作数,而是将来自操作 2 getResult() 的新结果包装在一个 Number 对象中。此外,位置 3 处的操作不再将表达式作为第一个操作数,而是将前一个操作的结果作为操作数。
在我看来,这种结构处理起来有点贵,我想问是否有人遇到同样的问题并提出了更好的解决方案,或者这个解决方案可能没问题。另一种方法是将完整的 Expression 存储为 String 并使用 RegExp 对其进行解析,以确定顺序的所有操作。这是一个更好的解决方案吗,因为我想让用户在输入表达式时动态更改操作数,即如果他写了前面的表达式:
3 + 4 * ((5 + 2) - √4 + sin(4) + 3²) / 2
如果他想在单击“等于”按钮执行操作之前将 √4 更改为 ln(6),他可以这样做。所以我想使用以前的 ArrayList 很难管理这样的变化,因为我需要保持我的表达式的每个操作数的位置......
例如如何谷歌在他的计算器应用程序中执行此操作,例如如果你看 -> https://www.google.it/search?q=2%2B2&oq=2%2B2&aqs=chrome.0.69i59j0l2j69i65l2j0.1141j0j7&sourceid=chrome&es_sm=94&ie=UTF-8#q=+3+%2B+4+*+((5+%2B+2)+-+sin(4)+%2B+3)+%2F+2
我知道它是 Javascript,但我猜每种语言的逻辑都是相同的。
您认为可能的解决方案是什么?感谢您的关注!
编辑:我不明白的东西:
/**
Evaluates a simple expression (such as "1+1") and returns its value.
@throws SyntaxException in these cases:
<ul>
<li> the expression is not well-formed
<li> the expression is a definition (such as "a=1+1")
<li> the expression is an implicit function (such as "x+1")
</ul>
*/
public synchronized double eval(String expression) throws SyntaxException {
return compiler.compileSimple(this, expression).eval();
}
该方法调用Compiler编译对象的.compileSimple:
Function compileSimple(Symbols symbols, String expression) throws SyntaxException {
rpn.setConsumer(simpleCodeGen.setSymbols(symbols));
lexer.scan(expression, rpn);
return simpleCodeGen.getFun();
}
它返回一个 Function 对象,然后对其调用 eval() 方法。查看 Function.eval() 方法我看到了这个:
/**
Evaluates an arity-0 function (a function with no arguments).
@return the value of the function
*/
public double eval() {
throw new ArityException(0);
}
方法 eval 必须返回一个 double 类型,并且实现抛出一个 ArityException 具有这个实现:
public class ArityException extends RuntimeException {
public ArityException(String mes) {
super(mes);
}
public ArityException(int nArgs) {
this("Didn't expect " + nArgs + " arguments");
}
}
如果抛出 ArityException,它如何评估字符串并返回一个双精度值?
【问题讨论】:
标签: java android calculator