使用Qt和Interpreter设计模式开发计算器(附源码)
2011-10-14 07:17
309 查看
计算器软件其实有很多种,但是基本上都是模仿计算器,用鼠标点击按键来操作,这次我们反其道而行之,采用类似文本输入的操作方式。
功能
1.键盘输入算式,回车后计算结果。
2.根据当前输入的函数的一部分,自动找到备选函数。这时可以用上/下键选择需要的函数后,按空格键确定输入。在整个过程中一直可以表示函数的帮助信息。我们可以参考帮助信息,选择合适的函数。
3.支持三角函数,反三角函数,求和,平均值,乘方,开方,对数,当然还有包含嵌套的四则运算。
执行画面如下
技术要点:
除了操作界面以外,实际我们是要做这样一个解析器,就面临这一个如何描述我们所面的需求的问题。在这里我们使用EBNF的一种形式,它由W3C定义。我们可以在XMLPathLanguage(XPath)2.0(SecondEdition)中找到它的细节。
一下是我们在计算器中输入的表达式的描述。
Token解析
解析算式也好,脚本也罢,在分析语法之前,一般都会引入词法分析的过程。简单说比如我们有如下算式
为了处理简单,在真正计算之前都会先将输入分解成一个个的单词(Token)。比如上面的内容经过处理,如果能变成下面的形式,就容易处理了。
这里,类似于acosd,cosd和(,),*之类的要素可以通过简单的字符串比较来解决。但是数字还有最后e指数就没有那么简单了。当然如果你认为这正好是展现编程基本功的机会的话,没有人拦着你,但是在本软件中我们采用了正则表达式来解决这个问题。
例如e指数可以如下定义:
对于一般形式的小数:
整数就更简单了。
这样一来我们就可以象处理+/-号一样处理其他要素了。可以很明显的看出,这里的内容和EBNF的内容很相似,也不知道是谁跟谁学的。
表达式解析
表达式解析的程序结果基本上就是参照EBNF的内容在加上一些归纳。主要功能有:
1.表达式解析通过buildExpr实现
2.表达式计算通过evaluate实现。
这里只提供类图,详细可以参照代码。
如何扩展自己的函数
计算器功能强大与否,主要是看函数功能是否强大。所以在设计的最初就把能够简单的扩展函数功能作为关键的设计目标之一。有没有做到呢?实际看一下代码应该最能说明问题。
先是头文件。
#ifndefACOSDFUN_H
代码是16行,实际上需要动的,只有类名和防止重复引用的宏定义,算4行吧。
然后是实现部分。
#include"acosdfun.h"
这个类是角度单位的反余弦函数的实现。空行算在内也只有38行。
第6行可能比较费解,是用来登录函数的。有了这一行,就能在函数列表中看到自己定义的函数了。
如果是简单的函数的话,相信15分种可以搞定。
只要在工程文件加入两个文件就可以扩展函数功能,不能再简单了吧。
相关资源:
可执行文件,可直接在WindowsXP,Windows7环境下执行:http://download.csdn.net/detail/craftsman1970/3682379
源代码,工程文件:http://download.csdn.net/detail/craftsman1970/3682385
功能
1.键盘输入算式,回车后计算结果。
2.根据当前输入的函数的一部分,自动找到备选函数。这时可以用上/下键选择需要的函数后,按空格键确定输入。在整个过程中一直可以表示函数的帮助信息。我们可以参考帮助信息,选择合适的函数。
3.支持三角函数,反三角函数,求和,平均值,乘方,开方,对数,当然还有包含嵌套的四则运算。
执行画面如下
技术要点:
除了操作界面以外,实际我们是要做这样一个解析器,就面临这一个如何描述我们所面的需求的问题。在这里我们使用EBNF的一种形式,它由W3C定义。我们可以在
一下是我们在计算器中输入的表达式的描述。
[1]Expr::=AdditiveExpr [2]AdditiveExpr::=MultiplicativeExpr(("+"|"-")MultiplicativeExpr)* [3]MultiplicativeExpr::=UnaryExpr(("*"|"/"|"%")UnaryExpr)* [4]UnaryExpr::=("-"|"+")*PrimaryExpr [5]PrimaryExpr::=NumericLiteral|ParenthesizedExpr|FunctionCall [6]NumericLiteral::=IntegerLiteral|DecimalLiteral|DoubleLiteral [7]ParenthesizedExpr::="("Expr?")" [8]FunctionCall::=FunctionName"("(Expr(","Expr)*)?")" [9]IntegerLiteral::=Digits [10]DecimalLiteral::=("."Digits)|(Digits"."[0-9]*) [11]DoubleLiteral::=(("."Digits)|(Digits("."[0-9]*)?))[eE][+-]?Digits [12]Digits::=[0-9]+ [13]FunctionName=sinr |sind |cosr |sind |tanr |tand |asinr |asind |acosr |acosd |atanr |atand |power |root
Token解析
解析算式也好,脚本也罢,在分析语法之前,一般都会引入词法分析的过程。简单说比如我们有如下算式
acosd(2)+cosd(30)*12+1.23E-21 |
acosd,(,2,),+,cosd,(,30,),*,12+1.23E-21 |
例如e指数可以如下定义:
"((\\.[0-9]+)|([0-9]+(\\.[0-9]*)?))[eE][+-]?[0-9]+" |
"(\\.[0-9]+)|([0-9]+\\.[0-9]*)" |
"[0-9]+" |
表达式解析
表达式解析的程序结果基本上就是参照EBNF的内容在加上一些归纳。主要功能有:
1.表达式解析通过buildExpr实现
2.表达式计算通过evaluate实现。
这里只提供类图,详细可以参照代码。
如何扩展自己的函数
计算器功能强大与否,主要是看函数功能是否强大。所以在设计的最初就把能够简单的扩展函数功能作为关键的设计目标之一。有没有做到呢?实际看一下代码应该最能说明问题。
先是头文件。
#ifndefACOSDFUN_H
#defineACOSDFUN_H
#include"calculatefunction.h"
classAcosdFun:publicCalculateFunction
{
public:
AcosdFun();
virtualQStringgetName();
virtualQStringgetInstruction();
virtualboolexecute(QList<double>paraList,double&result,QString&message);
};
#endif//ACOSDFUN_H
代码是16行,实际上需要动的,只有类名和防止重复引用的宏定义,算4行吧。
然后是实现部分。
#include"acosdfun.h"
#include"functionmanager.h"
#include<math.h>
staticFunctionManager::FunctionRegisteracosRegister(newAcosdFun());
AcosdFun::AcosdFun()
{
}
QStringAcosdFun::getName()
{
return"acosd";
}
QStringAcosdFun::getInstruction()
{
QStringstrInstruction;
strInstruction+="acosd(x)-\r\n";
strInstruction+="Arccosine(inversecosine)function.Resultindegrees.\r\n";
strInstruction+="Argumenttypeandattributes\r\n";
strInstruction+="xmustbeoftypereal.Itsvaluemustsatisfytheinequality|X|<=1.\r\n";
returnstrInstruction;
}
boolAcosdFun::execute(QList<double>paraList,double&result,QString&message)
{
if(paraList.count()!=1)
{
message="Acosd:Invalidparametercount";
returnfalse;
}
doublepara=paraList.first();
clearError();
result=acos(para)*180/M_PI;
returncheckError(message);
}
这个类是角度单位的反余弦函数的实现。空行算在内也只有38行。
第6行可能比较费解,是用来登录函数的。有了这一行,就能在函数列表中看到自己定义的函数了。
如果是简单的函数的话,相信15分种可以搞定。
只要在工程文件加入两个文件就可以扩展函数功能,不能再简单了吧。
相关资源:
可执行文件,可直接在WindowsXP,Windows7环境下执行:
源代码,工程文件:
相关文章推荐
- 设计模式--spring源码中使用策略模式(Strategy Pattern)
- 《GOF设计模式》—解释器 (INTERPRETER)—Delphi源码示例:使用Flyweight模式共享终结符
- 章节 1.3 极限编程 – 灵活,可靠的软件 使用设计模式和敏捷开发
- Android应用开发中控制反转IoC设计模式使用教程
- 游戏开发设计模式之原型模式 & unity3d JSON的使用(unity3d 示例实现)
- Mybatis3源码分析(08)-加载Configuration-使用到的设计模式
- iOS App设计模式开发之适配器模式使用的实战演练
- 设计模式--spring security源码中使用代理模式(Proxy Pattern)原创
- iOS应用开发中使用设计模式中的观察者模式的实例
- Java开发中的23种设计模式详解【带有源码】
- 游戏开发设计模式之原型模式 & unity3d JSON的使用(unity3d 示例实现)
- Android设计模式——策略模式之源码使用场景(三)
- 敏捷方法 – 灵活,可靠的软件 使用设计模式和敏捷开发
- 数值计算库中使用设计模式(一)
- Android设计模式——单例模式之源码使用场景(一)
- Java设计模式开发中使用观察者模式的实例教程
- 项目开发中的一些注意事项以及技巧总结 基于Repository模式设计项目架构—你可以参考的项目架构设计 Asp.Net Core中使用RSA加密 EF Core中的多对多映射如何实现? asp.net core下的如何给网站做安全设置 获取服务端https证书 Js异常捕获
- 设计模式学习(十四)————抽象工厂模式(使用Qt框架的反射技术——根据字符串动态创建类来实现)
- 在WindowsPhone开发中使用MVVM设计模式
- IOS开发:基本设计模式(下)-使用设计模式解决问题