如何写一个计算器

考虑这样一个问题,给定一个字符串,“1+1+(3+4)-2*3+8/2”,如何将它转化为如下形式,

“1+1=2”
“3+4=7”
“2+7=9”
“2*3=6”
“9-6=3”
“8/2=4”
“3+4=7”

换句话说,就是如何将字符串按照四则运算计算出来,如何写一个计算器。
拿java来举例,并且为了简单,我们只考虑个位数。这个过程大概分为这几个步骤,首先需要扫描字符串去除空白字符,其次将各个字符转换成对应的操作符或操作数,然后按照四则运算规则逐次计算并输出。

好,我们首先构造一个scanner,它主要功能是顺序扫描字符串,返回字符并跳过其中的空白字符,如下

public class Scanner {

    public Scanner(String source){
       this.source = source.toCharArray();
    }

    private char[] source;
    private int index = 0;
    private static char END = ‘\n‘;
    public char getNext(){
        char result;

        do{
            if (index >= source.length){
                return END;
            }
            result = source[index];
            index += 1;
        }while (Character.isWhitespace(result));

        return result;
    }

}

在进行下一步之前,让我们思考一下这个算式的规律,算式中存在两种对象,一种是数字,一种是操作符,由于存在运算的优先级,我们分成三种对象,并用下面的形式来说明,

expr —> term + expr | term - expr | term
term —> factor * term | factor/term | factor
factor—> digit |(expr)

‘—>’的意思是’由...组成’,’|’ 代表’或关系’,expr代表加减法运算式,term代表乘除法运算式,factor代表操作的最小元素,最后一句的意思就是factor由数字或者带括号的expr组成。这三个定义式是递归的,它可以代表任意深度的算式。让我们用树的形式来观察一下,

有了这三种抽象对象我们可以写出对应方法了,我们在parser类里定义三个函数,来代表三种对象的产生过程,并且定义char类型变量head代表正在被扫描的字符。

public class Parser {
    private Scanner scanner;
    public Parser(Scanner scanner){
        this.scanner = scanner;
    }

    private char head;

    public void parse(){
        if (Scanner.END != (head = scanner.getNext())){
            expr();
        }
        if (head != Scanner.END){
            throw new RuntimeException("syntax error at "+head);
        }
    }

    public int expr(){
        int result = term();
        int tempResult;
        char operate;
        while ((operate = head) == ‘+‘ || operate == ‘-‘) {
            head = scanner.getNext();
            tempResult = term();
            switch (operate) {
                case ‘+‘:
                    System.out.println(result + "+" + tempResult + "=" + (result + tempResult));
                    result += tempResult;
                    break;
                case ‘-‘:
                    System.out.println(result + "-" + tempResult + "=" + (result - tempResult));
                    result -= tempResult;
            }
        }
        return result;
    }
    public int term(){
        int result = factor();
        int tempResult;
        char operate ;
        while ((operate=head) == ‘*‘ ||operate == ‘/‘) {
            head = scanner.getNext();
            tempResult = factor();
            switch (operate) {
                case ‘*‘:
                    System.out.println(result + "*" + tempResult + "=" + (result * tempResult));
                    result *= tempResult;
                    break;
                case ‘/‘:
                    System.out.println(result + "/" + tempResult + "=" + (result / tempResult));
                    result /= tempResult;
            }
        }
        return result;
    }

    public int factor(){
        int factor;

        if (Character.isDigit(head)){
            factor = head - 48; //字符变量-48可以转换成对应的字面数字
            head = scanner.getNext();
        } else {
            match(‘(‘);
            factor = expr();
            match(‘)‘);

        }
        return factor;
    }

//match方法用来断言head是什么字符,如果为真,将读取下一个字符赋值给head
    public boolean match(char symbol){
        if (symbol == head){
            head = scanner.getNext();
            return true;
        }
        throw new  RuntimeException("syntax error at "+head);
    }

    public static void main(String... args){
        Scanner scanner = new Scanner("1+1+(3+4)-2*3+8/2");
        Parser parser = new Parser(scanner);
        parser.parse();
    }
}

如果回过头来重新考虑这件事情,你会发现我们这个小程序的本质是将一段文本转化成可以执行的程序,正如我们的编译器一样。而实际上编译器要复杂的多,它的基本工作过程可以分为几个步骤:

  1. 词法分析(scanning),读入源程序字符流,将字符转换成有意义的词素(lexeme)的序列,并生成对应的词法单元(token)
  2. 语法分析(parsing),主要目的是生成词法单元的语法结构,一般会使用树形结构来表示,称为语法树。
  3. 语义分析(semantic analysis),使用语法树检查源程序是否和语言定义的语义一致。其中一个重要部分是类型检查。
  4. 生成中间代码,语义分析完成后,编译器会将语法树生成为一种接近机器语言的中间代码。我们程序最后产生的一系列小的表达式与之类似。
  5. 代码优化,编译器会尝试改进中间代码,用以生成更高效的机器代码。
  6. 代码生成,将优化过对中间代码生成机器代码。

在这些过程中,递归的方法起到了非常重要的作用,有一句话说明了编译器的本质,编译器就是让你的源程序变成可执行程序的另一个程序。你会发现这个定义本身就是递归的。透过这些编译原理,可以让我们更加深入的理解编程语言,甚至发明一种编程语言。

原文地址:http://blog.51cto.com/abiton/2115404

时间: 2024-10-06 22:43:49

如何写一个计算器的相关文章

利用面向对象写一个计算器

本文参考了程杰的<大话设计模式>,使用C#语言利用面向对象的模式来写一个计算器. 如果是我本人来写计算器,也就加减乘除的话,估计我会全部写进控制台里面,写4个if语句出来......或者我会利用switch,但是这样的效果都不好,有更好的方法,就是如下所示的代码啦: 先定义一个Operation类,主要实现运算框架: class Operation { public double Number1 { get; set;} public double Number2 { get; set; }

用VBA写一个计算器

着急的 玩家 可以 跳过“============”部分 ======================================可以跳过的 部分   开始====================================================== 之所以 要有今天这样一篇博客是因为. 之前领导让找几组数据.从excel表格里面. 要求是 主号码 一致,副号码 一致,名称为 XXX 的 项目发生变化的数据 需要被找到. 这样的 项目 有 11个. 我找的真的是眼要花. 变动前

用PHP写一个计算器

本篇文章介绍了使用PHP+HTML+Javascript技术编写一个计算器的方法,希望对各位学习PHP开发的同学有帮助! 用PHP写一个计算器 <body> <?php if (!empty($_POST)) { $op=$_POST['point']; $sum1 = $_POST['sum1']; $sum2 = $_POST['sum2']; $sum = 0; if ($sum1 != '' && is_numeric($sum1) && $sum

今天学习了在一般处理程序中写一个计算器

今天看了看前面的javascript以及学习的jquery,感觉刚开始学习时间就赶得比较紧,练习是做啦,基础的知识都是知道的,但是在写一些小小的程序时间还是陌生,感觉练习的还是很不够的,其实怎么说那,想好好的学习还是把他们使用到项目中练习就会学习的更快些吧,然后我又做啦一个增删改查的一般处理程序,但是还没有总结好,现在就简单的先总结一个小小的计算器使用html和ashx来实现一下其功能,如下所示: 一.html样式 <script> //@Script; </script> <

用JS写一个计算器

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"><html xmlns="http://www.w3.org/1999/xhtml"><head><meta http-equiv="Content-Typ

1.自己写一个计算器demo

知识点: 1.System.Math.Pow() 实现乘方 2.实现计算器的运算优先级,依次调用的流程 问题: 还未实现“()”功能 解决方案 UI: 1 using System; 2 using System.Collections.Generic; 3 using System.ComponentModel; 4 using System.Data; 5 using System.Drawing; 6 using System.Text; 7 using System.Windows.Fo

用JS写一个计算器(兼容手机端)

先看成果:1.PC端2. 首先确立html,有哪些东西我们要知道.布局大概的样子在心里有个数 <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"><title>瑞雪的计算器</t

五一放假作业4.30 用正则表达式写一个计算器!去掉括号,计算式子结果!

作业要求:去掉括号,计算式子结果. 1 - 2 * ( (60-30 +(-9-2-5-2*3-5/3-40*4/2-3/5+6*3) * (-9-2-5-2*5/3 + 7 /3*99/4*2998 +10 * 568/14 )) - (-4*3)/ (16-3*2) ) #!/usr/bin/env python# -*- coding: utf-8 -*-# @Time : 2017/4/30 7:44# @Author : Olivia Su# @File : 作业.py# @Softw

PyQt5 写一个计算器框架

import sys from PyQt5.QtWidgets import QWidget, QLabel, QApplication, QPushButton, QHBoxLayout, QVBoxLayout, QGridLayout class Example(QWidget): def __init__(self): super().__init__() self.initUI() #---------------------------------------------------