在这篇文章中,将探讨如何使用C++构建一个功能齐全的计算器应用程序。这个计算器将支持不同的数值系统、括号以及科学计算器上可用的函数。
去年的一个不眠之夜,自问:计算器是如何工作的?那时正在为Jump’n’Run框架开发一个基于栈的小型脚本引擎。完成后,思考是否可以使用栈来实现计算器。第二天,在网上搜索计算器的工作原理,但没有找到很好的解释(实际上直到今天都不知道方法是否是通用方法,或者是否有更好或更简单的方法)。因此,决定尝试一下。在对计算器一无所知的情况下,简单地开始分析计算器的行为。
最感兴趣的问题是,如果不知道整个表达式,那么计算一个项的规则是什么。让给展示这个问题:
要计算这个表达式:
6*3+2*2+(2+3)
要计算这个表达式,可以使用这种方式:
可以这样做的一个原因是,从一开始就知道整个表达式。比一个只输入了表达式到n位置的计算器处于更好的情况。尽管如此,计算器在不知道整个表达式的情况下就开始计算。这是如何工作的?这就是想问的问题。
开始分析计算器(TI-30Xa)的行为,并发现了一些要点:
在这一点上,只关心这些操作:+, -, *, /, (, )。
在输入数字时,显示的数字并不真正表现得像一个数字。它更像是一个字符串而不是一个数字。
按下数字键不会引起任何计算。
按下操作符键有时会触发计算。
每次按下操作符键后都可能发生计算。
现在让更仔细地看看这些要点。
当开始输入一个数字时,显示的数字表现得像一个字符串。让尝试在计算器上输入1024。开始按下#1, #0, #2和#5。这里发生了什么?这些数字被连接起来了。嗯,想打错了一个数字。显示屏显示1025而不是1024。没问题,使用退格键,显示屏显示102。数字5被切断了。现在按下#4,看到1024。
想明白所说的“表现得像一个字符串”是什么意思。
关键是只要显示屏上的数字没有用于计算,它就是一个字符串。
没什么好说的,试试就知道了。可以输入任何想要的数字,计算器永远不会计算任何东西。
写下一些表达式并在计算器上输入它们。每当看到任何计算时,在笔记中相应的位置标记一个“|”。
例子:
6*2+|3+|4*2=|
1024/2*|(2+2)|-|1+|2=|
7+2*4*|0,5=|
看到了什么?#(永远不会触发计算。目前,不知道这个键实际上做了什么。
每当进行了计算,按下了以下键之一:#+, #-, #*, #/, #), #=。
从这个角度来看,将它们全部称为操作符。对于这种不精确性,感到抱歉,因为从数学角度来看,#(和#)并不是操作符。
目前没有看到任何规则,哪个操作符在哪个位置会触发计算。这比说每当按下#*就进行计算要复杂得多。看看第一个表达式“...4*2=”,这里它没有触发计算,而在最后一个表达式“7+2*4*”中,最后一个确实触发了计算。但到目前为止,有以下规则:
通过试错开发了这个系统。开始了一个简单的想法,将数字和操作符推到它们的栈上,然后尝试计算表达式。如果看到这个系统失败了,就会找出它为什么不工作,并进行更改。过了一会儿,喝了几杯茶之后,它变得越来越好。最后它起作用了。
这就是它:
首先,每个操作符都有一个优先级:
接下来需要两个栈,一个用于存储数字,一个用于存储操作符。
此外,需要一个变量来存储最后输入的操作符的优先级(让称之为pol)。
最后,一个要计算的表达式:6+2*3-(2+3)=
要计算这个表达式,必须使用这些工具,并结合以下规则。
源代码可能如下所示:
var op: String = _opstack.pop().toString();
var p2: Number = Number(_numberStack.pop());
var p1: Number = Number(_numberStack.pop());
switch (op) {
case "/":
_numberStack.push(p1 / p2);
break;
case "*":
_numberStack.push(p1 * p2);
break;
case "-":
_numberStack.push(p1 - p2);
break;
case "+":
_numberStack.push(p1 + p2);
break;
}
看看p1和p2。p2是第一个输入的数字,现在是栈上最上面的数字。知道10-2 != 2-10。
认为已经得到了所有需要的东西了吗?不,还有一个最后一点。如果进行计算,不必进行一次计算,必须进行尽可能多的计算。这是关注pol的地方。开始计算,如果当前按下的操作符的优先级小于或等于操作符栈上最上面的操作符的优先级,并重复计算,只要pol(最后输入的操作符的优先级)高于或等于操作符栈上最上面的操作符的优先级。
源代码可能如下所示:
while (
getPriority(nop) <= getPriority(_opstack[_opstack.length - 1]) &&
_pol >= getPriority(_opstack[_opstack.length - 1]) &&
_opstack[_opstack.length - 1] != "("
) {
... calculation
}
就是这样!这是计算器的非常基本的系统。
当然,还有更多要说的,例如:
3*2+((2-3)+4=
可能想知道为什么写了这篇文章。去年用Actionscript在Flash中写了一个计算器。但目前正在学习C++和wxWidgets,为此,需要一个小项目来实现。这就是重写这个计算器的原因。