大家好我是Android新手,我正在尝试构建一个中等难度的计算器应用程序。我想出了这种结构,但没有实现任何东西,因为我有点怀疑,如果这可能是正确的。所以在这里我去:Android计算器应用程序的良好做法?
我有一个操作数接口那种所谓的“的getValue()”方法应该返回一个double类型,以及实现该接口的一些其他对象,从而实现一个“的getValue() “方法:
表达,我想过使之成为一个对象与将存储所有这些操作的私有成员字段,所以这个表达式对象将有这样的事情:
public class Expression implements Operand { private List<Operation> operationList; ... }
operationList将是一个ArrayList,其中Operation是另一个涉及两个操作数(因此有两个操作数接口类型)的对象,它们与一个操作符对象绑定在一起,该操作符对象的枚举类型说明涉及这两个操作数的操作是否为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
数类,它实现操作数,这将是一个双重价值一个简单的包装类,并且将返回双发中它实现的getValue()方法。
带枚举类型的函数类,如果它是SIN,COS,TAN,ARCSIN,ARCCOS,ARCTAN,LOG或LN函数,则始终使用getValue()方法返回计算的双重结果功能;
实用程序,这个类可能会被误解:它是类指针,平方根,数字百分比或阶乘等操作。这里的想法是再次使用枚举类型来区分操作的类型。我知道指数运算本身就是一个操作,但对我来说,保持它远离之前解释的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(项目N°2)必须在操作数1之前进行,但我觉得这样不太好原因你会发现我需要整理所有的ArrayList,每次当Operation N°2被执行时得到操作结果并得到双重结果时,我需要为该结果创建一个Number()对象以再次处理它就像操作数一样,重新组织ArrayList的方式是操作号不再是Number(4)作为第二个操作数,但是具有包装在Number对象中的Operation 2 getResult()的新结果。此外,位置3处的操作不再具有作为第一操作数的表达式,而是将前一操作的结果作为操作数。
在我看来,这种结构有点昂贵的处理,我问如果有人面临同样的问题,并提出了一个更好的解决方案,或者这种解决方案可能没问题。另一种方法是将完整的Expression存储为String,并使用RegExp对其进行解析,以确定所有操作的顺序。它是一个更好的解决办法,因为我想,让用户更改操作数的飞行,如果当他打字的表达,也就是说,如果他写的前一个表达式:
3 + 4 * ((5 + 2) - √4 + sin(4) + 3²)/2
他可以改变√4到ln(6)如果他想在点击“Equals”按钮执行操作之前做到这一点。所以我想用先前的ArrayList来管理这种变化是很困难的,因为我需要保留我的表达式的每个操作数的位置...
Google在他的计算器应用程序中这样做如果你看 - >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();
}
此方法调用编译器来编译对象的.compileSimple:
Function compileSimple(Symbols symbols, String expression) throws SyntaxException {
rpn.setConsumer(simpleCodeGen.setSymbols(symbols));
lexer.scan(expression, rpn);
return simpleCodeGen.getFun();
}
返回一个函数对象,然后调用eval()方法。看功能。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,那么是double?
感谢您的链接,我看到它使用所谓的元数一个JAR库,这需要一个表达式字符串和评估它像一个操作。所以我可以假设我可能应该实现kinda .eval()函数来在用户最终按下相等按钮时评估完整表达式字符串? – tonix
是的,它使用arity引擎。实际上许多计算器应用程序都使用该库。我编辑了答案以获取更多信息 –
感谢分享,我查看了Arity代码,并且无法理解一件事情:它如何通过操作来处理字符串?如果您查看Symbols对象的方法eval(),则会发现它调用Compiler member var的** compiler.compileSimple(this,expression)**,该变量反过来返回一个* Function *对象,并在该对象上一个* eval()*方法被调用,但该方法的实现抛出一个* ArityException *类型的异常,并且不会返回一个double ...请检查我的编辑。 – tonix