2014-06-18 30 views
3

大家好我是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?

回答

2

您可能想看看与平台打包在一起的官方Android计算器应用程序的源代码。

我认为Logic.java将是你正在寻找的类。它具有用于格式化的代码,检查运营商,估值等:Android Calculator

EDIT

Android的计算器使用元数算术引擎,这是用于评价表示为字符串算术表达式的开源库。我无法找到项目的活动链接,它已从code.google.com中删除。但是你可以参考以下链接,了解更多信息:

  1. http://www.developerfusion.com/project/62854/arity/
  2. Download arity .jar
+0

感谢您的链接,我看到它使用所谓的元数一个JAR库,这需要一个表达式字符串和评估它像一个操作。所以我可以假设我可能应该实现kinda .eval()函数来在用户最终按下相等按钮时评估完整表达式字符串? – tonix

+0

是的,它使用arity引擎。实际上许多计算器应用程序都使用该库。我编辑了答案以获取更多信息 –

+0

感谢分享,我查看了Arity代码,并且无法理解一件事情:它如何通过操作来处理字符串?如果您查看Symbols对象的方法eval(),则会发现它调用Compiler member var的** compiler.compileSimple(this,expression)**,该变量反过来返回一个* Function *对象,并在该对象上一个* eval()*方法被调用,但该方法的实现抛出一个* ArityException *类型的异常,并且不会返回一个double ...请检查我的编辑。 – tonix