Django开发微信小程序之逆波兰计算器(二)计算器实现算法

下面将着重讲解如何实现计算器,使其可以进行小数、负数的加减乘除计算,并显示结果给用户

处理用户输入

用户输入一长串的表达式,里面有数字,有加减乘除符号,有括号,我们应该把用户输入的表达式记录下来,并进行适当的划分,将数字和符号分开来,为后续的计算做准备

首先我们在为每个按钮都设置一个data-value自定义属性,从而在后台知道用户按了哪一个键

<view class="button_area" bindtap='clickButton'>
    <view class="row">
      <button class="row1-item button_item" data-value="7" hover-stay-time='120'>7</button>
      <button class="row1-item button_item" data-value="8" hover-stay-time='120'>8</button>
      <button class="row1-item button_item" data-value="9" hover-stay-time='120'>9</button>
      <button class="row1-item button_item" data-value="/" hover-stay-time='120'>/</button>
      <button class="row1-item button_item" data-value="back" hover-stay-time='120'></button>
    </view>
    <view class="row">
      <button class="row1-item button_item" data-value="4" hover-stay-time='120'>4</button>
      <button class="row1-item button_item" data-value="5" hover-stay-time='120'>5</button>
      <button class="row1-item button_item" data-value="6" hover-stay-time='120'>6</button>
      <button class="row1-item button_item" data-value="*" hover-stay-time='120'>*</button>
      <button class="row1-item button_item" data-value="." hover-stay-time='120'>.</button>
    </view>
    <button class="button_item equal" data-value="=" hover-stay-time='120'>=</button>
    <view class="row short">
      <button class="row1-item button_item" data-value="1" hover-stay-time='120'>1</button>
      <button class="row1-item button_item" data-value="2" hover-stay-time='120'>2</button>
      <button class="row1-item button_item" data-value="3" hover-stay-time='120'>3</button>
      <button class="row1-item button_item" data-value="-" hover-stay-time='120'>-</button>
    </view>
    <view class="row short">
      <button class="row1-item button_item" data-value="0" hover-stay-time='120'>0</button>
      <button class="row1-item button_item brace" data-value="(" hover-stay-time='120'>(</button>
      <button class="row1-item button_item brace" data-value=")" hover-stay-time='120'>)</button>
      <button class="row1-item button_item" data-value="+" hover-stay-time='120'>+</button>
    </view>
  </view>

在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',')']
  • 0前面没有数字或符号时,应不许输入,防止出现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函数,显示给用户,并把将存储表达式两个数组清空,将结果存入后,等待用户后续计算的输入,一次完整的计算过程就已经完成了。

猜你喜欢

转载自blog.csdn.net/zjw_python/article/details/80721429