下面将着重讲解如何实现计算器,使其可以进行小数、负数的加减乘除计算,并显示结果给用户
用户输入一长串的表达式,里面有数字,有加减乘除符号,有括号,我们应该把用户输入的表达式记录下来,并进行适当的划分,将数字和符号分开来,为后续的计算做准备
首先我们在为每个按钮都设置一个data-value自定义属性,从而在后台知道用户按了哪一个键
在js文件中,我们使用两个数组来保存用户输入的表达式,一个用于显示给用户看,一个用于实际计算,你也可以只使用一个数组,看个人喜好。
data: {
expression:{
ForCustomer:[], //用于显示给用户,存储用户输入的数据
ForCompute:[] //用于进行计算,存储转换后的输入数据
},
value:0, //文本框的显示值
},
下面我们就开始处理用户输入,主要在clickButton事件程序中,主要思路就是把用户每个输入都依次存入到ForCustomer
中,而把数字和符号划分后的输入存入到ForCompute中。在进行表达式划分时,主要遵循以下原则:
1*20-3+40
,那么ForCustomer
的值应该为['1','*','2','0','-','3','+','4','0']
,而ForCompute
的值应该为['1','*','20','-','3','+','40']
1.1+0.3
,那么ForCompute
的值应该为['1.1','+','0.3']
-3-5+(-6)
,那么ForCompute
的值应该为['-3','-','5','+','(','-6',')']
01+1
这样的表达式下面就是clickButton的代码,对用户每次输入进行判定,执行相应的操作
clickButton: function(event){
var button_value = event.target.dataset.value,
show_value = '',
result_value = 'none',
expression_show = this.data.expression.ForCustomer,
expression_value = this.data.expression.ForCompute,
var length = expression_value.length,
show_length = expression_show.length,
zero_flag = true;
if (button_value){
switch (button_value) {
case 'back': //用户点击后退删除按钮
if (show_length >= 1){
expression_show.pop();
if (expression_value[length - 1].length > 1 && expression_value[length - 1] != 'ERROR' && expression_value[length - 1] != '0不能做除数'){
expression_value[length - 1] = expression_value[length - 1].substr(0, expression_value [length - 1].length -1);
}else{
expression_value.pop();
}
if (show_length == 1 ){
show_value = '0';
}
}
break;
case '=': //用户点击等于按钮,进行计算
if (length > 2){
result_value = this.calculate(expression_value);
if (result_value == 'zero'){
result_value = '0不能做除数';
}else if (isNaN(result_value)){ //若计算结果非数字,则出现错误
result_value = 'ERROR';
}
}
break;
default:
if (isNaN(button_value)){ //用户点击非数字按钮
if (button_value == '.' && !isNaN(expression_value[length - 1])) { //处理小数点问题
expression_value[length - 1] += button_value;
} else {
expression_value.push(button_value);
}
} else { //用户点击数字按钮
if (expression_value[length - 1] == '.' || ! isNaN(expression_value[length - 1])){ //处理小数点问题
expression_value[length - 1] += button_value;
} else if (expression_value[length - 1] == '-' && (length < 2 || expression_value[length - 2] == '(')) { //处理负号问题
expression_value[length - 1] += button_value;
} else if (button_value == '0' && length == 0){ //0不能作为表达式开头
zero_flag = false;
} else {
expression_value.push(button_value);
}
}
if (zero_flag){
expression_show.push(button_value);
}
break;
}
if (show_value !== '0'){ //将表达式数组转换为字符串
show_value = expression_show.join('');
}
if (result_value !== 'none'){ //当有计算结果时,清空输入的数据,并将计算结果显示
show_value = result_value;
expression_show.length = 0;
expression_value.length = 0;
expression_show.push(result_value);
expression_value.push(result_value);
}
if (show_value){ //显示结果
this.setData({
value: show_value
});
}
}
},
经过上述的输入处理,用户输完表达式后,我们就可以得到相应的表达式数组。例如用户输入-0.1+(3-0.5)*2
,此时ForCustomer
的值应该为['-','0','.','1','+','(','3','-','0','.','5',')','*','2']
,而ForCompute
的值应该为['-0.1','+','(','3','-','0.5',')','*','2']
。我们计算只需要ForCompute
的值。**我们主要使用的是逆波兰算法,在此过程利用栈,先将中缀表达式转换为后缀表达式,再计算后缀表达式的值。该算法作为基本的算法,需要掌握,不清楚的自行百度。**有一种更简单的方法,就是直接传入表达式字符串,使用eval()
函数计算表达式的值,但这样就太没有意思了。
将处理后的表达式数组传入calculate函数进行计算
calculate: function(expressions){
var transfer_stack = [], //用于中缀表达式转后缀表达式
temp_stack = [], //用于存储中缀表达式
calculate_stack = [], //用于计算中缀表达式
result = 0, //计算的结果
priority_table = { //符号优先级表
'*' : 2,
'/' : 2,
'+' : 1,
'-' : 1,
'(' : 0,
')' : 3
};
/*
将中缀表达式转换为后缀表达式
*/
for (var value in expressions){
if (!isNaN(expressions[value])){ //如果是数字,直接输出
temp_stack.push(expressions[value]);
} else {
if (expressions[value] == ')'){ //如果是右括号,将左括号之后的项目全部出栈
while(transfer_stack.length != 0 && transfer_stack[transfer_stack.length-1] != '('){
temp_stack.push(transfer_stack.pop());
}
transfer_stack.pop();
} else if (expressions[value] != '(' && priority_table[expressions[value]] <= priority_table[transfer_stack[transfer_stack.length-1]]) { //如果符号不是左括号,且符号优先级不大于栈顶项目,则依次出栈,直到项目全部出栈或者栈顶项目优先级小于当前符号
temp_stack.push(transfer_stack.pop());
while (priority_table[expressions[value]] <= priority_table[transfer_stack[transfer_stack.length - 1]]){
temp_stack.push(transfer_stack.pop());
}
transfer_stack.push(expressions[value]);
} else { //如果符号是左括号,或者优先级高于栈顶元素,则进栈
transfer_stack.push(expressions[value]);
}
}
}
while(transfer_stack.length > 0){ //将剩余的项目全部出栈
temp_stack.push(transfer_stack.pop());
}
/*
计算后缀表达式
*/
for (value in temp_stack){
if ( !isNaN(temp_stack[value])){ //如果是数字直接进栈
calculate_stack.push(temp_stack[value]);
}else{ //如果是符号,则将栈顶两元素出栈,计算后,再将结果入栈
var num_1 = Number(calculate_stack.pop()),
num_2 = Number(calculate_stack.pop());
switch (temp_stack[value]){
case '+':
result = num_1 + num_2;
break;
case '-':
result = num_2 - num_1;
break;
case '*':
result = num_2 * num_1;
break;
default:
if (num_1 == 0){
result = 'zero';
}else{
result = num_2 / num_1;
}
break;
}
calculate_stack.push(result);
}
if (isNaN(result)){ //如果出现错误,则退出循环
break;
}
}
result = calculate_stack.pop(); //取出最终计算结果
return result;
},
计算完成后,就返回result结果给clickButton函数,显示给用户,并把将存储表达式两个数组清空,将结果存入后,等待用户后续计算的输入,一次完整的计算过程就已经完成了。