2016-2017-2 《Java 程序设计》课堂实践项目

目录

作了几年教学改革,理论和形式上我感受基本完备了:html

很重要的一点是厘清“教是老师的责任,学是学生的责任”,也就是“老师当教练,学生作中学”。java

有了SPOC平台蓝墨云班课 ,教学工具上也基本完善了:linux

我在程序设计学习上有一个基本的体会是:android

  • 开始不会编程,最好的学习方式是抄教材,敲代码,还专门写了一篇指导《积极主动敲代码,使用Junit学习Java程序设计》,我认为积极主动敲5000行左右的代码,应该能解决基本语法的问题,基本程序设计的问题,基本工具(git,jdb,junit,idea...)的使用问题git

  • 而后独立编写5000行左右的代码,解决程序逻辑错误的调试,模块分解,问题解决的通常过程等相关问题程序员

  • 有了10000行代码的基础,后面的学习提升要依靠代码阅读了,好比JUnit的源码,JHotdraw的源码,Java Collection API的源码,Java JCE的源码等web

教学中也是想经过这三个步骤进行训练。算法

看了范飞龙博士(博客微博)的“如何设计题目”和“近发展区/脚手架”,一方面感受龙博不当老师真是亏了,另外一方面是感受本身的题目设计上还有改进的空间。数据库

这篇指导想参考龙博的博客,解决第二层次的问题,从Hello World经过一个个小练习不断迭代,来一步一步提升你们使用Java来解决具体问题的能力。express

返回目录

基本工具

这里面的工具每周都要求使用,咱们同窗的博客上也都提交了会使用的证据,课上默认你们熟练掌握了,没有掌握的课下努力了。

返回目录

基础内容

这些内容在实验课上都作过了,同窗们也都提交了本身掌握了的证据,咱们在课堂实践测试时每次都会使用相关内容。

返回目录

Hello World 和 模块分解

由Kernighan和Ritchie合著的经典教程《The C Programming Language》的开篇第一个C程序例子是打印简单的“Hello World!”。今后以后,“Hello World”就成了描述人们编写的第一个程序的代名词---无论你用什么编程语言。

简单的“Hello World”可以运行出来,说明你的开发环境是没有问题的,这就有了进一步学习的基础。经过“Hello World”程序你也能初步理解程序的基本结构。

Java程序设计也不例外,咱们从"Hello World"起步,来看看你的开发环境是否是准备好了。若是开发环境都没有准备好,你号称本学期写了几千行代码基本上就能够归零了。

开发环境的准备有命令行和IDE两种,你们都要掌握,后面的练习会用到命令行和IDE,前面没有掌握好的复习上面的基础内容。

Java中的Hello World程序HelloWorld.java以下:

public class HelloWorld {
    public static void main(String[] args){
        System.out.println("Hello World!");
    }
}

我在Linux命令行中能够用javac HelloWorld.java进行编译上面的程序,用java HelloWorld运行上面的程序:

教材已经学习完毕,咱们知道Java中子系统是以包实现的,咱们如今写个带包的Hello World:

package ljp.is.besti.edu.cn;

public class HelloWorld {
    public static void main(String[] args){
        System.out.println("Hello World!");
    }
}

这时候,用javac编译时要加上-d参数,用java运行时类名前要加上包前缀:

为了方便管理实践考试代码,建议同窗们创建一个以本身学号命名的文件夹,每次实践考试都要提交这个文件夹的码云连接.
咱们养成在项目根目录下也就是2015520ljp文件夹下工做的习惯,这时候经过 javac -d bin src/HelloWorld.java把class文件编译到bin文件夹下,运行时要加上-cp或-classpath参数:

Java程序员有两个角色:

  • 类设计者
  • 类使用者

对一个模块,咱们要设计出来一个类,还要有一个测试类,这个测试类是一个带main的public类

  • XXXX.java
  • XXXXTester.java
    • 带main

第一个任务是设计一个HelloWorld类,里面有一个方法public static void printHello()打印出“Hello World”字符串,在HelloWorldTester类补充代码调用printHello。

HelloWorld.java:

package ljp.is.besti.edu.cn;

public class HelloWorld {
    public static void sayHello(){
        System.out.println("Hello World!");
    }
}

HelloWorldTester.java:

package ljp.is.besti.edu.cn;

public class HelloWorldTester {
    public static void main(String[] args){
        //补充代码调用printHello
        ...
    }
}

下面是补充好的代码和编译运行过程:

package ljp.is.besti.edu.cn;

public class HelloWorldTester {
    public static void main(String[] args){
        //补充代码调用printHello
        HelloWorld.sayHell();
    }
}

返回目录

数组的使用

算法设计中很重要的一点是临时变量的使用。好比,咱们定义了两个变量int a=5;int b=6; 咱们怎么把a和b的值交换一下? 很多程序设计的初学者的作法是:

a = b;
b = a;

这样是不能实现两个变量的交换的,执行完第一条语句a的值为6,b的值也为6,执行完第二条语句以后a的值为6,b的值也为6,没什么变化。

正确的作法是定义一个临时变量:

int tmp = a;
a = b;
b = tmp;

不使用临时变量也有办法实现交换,参考交换两个变量的值,不使用第三个变量的四种法方法,那样代码不清晰,咱们不提倡使用。

程序设计中有三种语句:

  • 顺序语句
  • 分支语句
  • 循环语句

咱们解决问题时要提升抽象能力,要多使用循环解决问题,分支只是用来解决特殊状况的。

经过使用数组来学习循环的使用。

Java中遍历数组,for-each语法很好用:

//定义一个数组,好比
  int arr[] = {1,2,3,4,5,6,7,8};
 
  //打印原始数组的值
  for(int item:arr){
      System.out.print(item + " ");
  }
  System.out.println();

咱们还能够从前日后遍历数组:

//定义一个数组,好比
  int arr[] = {1,2,3,4,5,6,7,8};
 
  //打印原始数组的值
  for(int i=0; i<arr.length; i++){
      System.out.print(arr[i] + " ");
  }
  System.out.println();

咱们还能够从后往前遍历数组:

//定义一个数组,好比
  int arr[] = {1,2,3,4,5,6,7,8};
 
  //打印原始数组的值
  for(int = arr.lenth; i>0; i--){
      System.out.print(arr[i-1] + " ");
  }
  System.out.println();

实践任务:

//定义一个数组,好比
  int arr[] = {1,2,3,4,5,6,7,8};
 
  //打印原始数组的值
  for(int i:arr){
      System.out.print(i + " ");
  }
  System.out.println();
  
  // 添加代码删除上面数组中的5
  ...
  
  //打印出 1 2 3 4 6 7 8 0
  for(int i:arr){
      System.out.print(i + " ");
  }
  System.out.println();
 
  // 添加代码再在4后面5
  ...
  
  //打印出 1 2 3 4 5 6 7 8
  for(int i:arr){
      System.out.print(i + " ");
  }
  System.out.println();

删除一个元素时比较容易,插入时就要用到临时变量了,固然,你能够从数组的最后一个元素开始移动,就避免了临时变量的使用。

参考代码:

1 public class ArrayOperation {
  2
  3     public static void main(String [] args){
  4         int arr[] = {1,2,3,4,5,6,7,8};
  5
  6         for(int i:arr){
  7             System.out.print(i + " ");
  8         }
  9         System.out.println();
 10
 11         for(int i=5; i<arr.length; i++)
 12             arr[i-1] = arr[i];
 13
 14         arr[7] = 0;
 15
 16         for(int i:arr){
 17             System.out.print(i + " ");
 18         }
 19         System.out.println();
 20
 21         for(int i=arr.length; i>4; i--)
 22             arr[i-1] = arr[i-2];
 23
 24         arr[4] = 5;
 25
 26         for(int i:arr){
 27             System.out.print(i + " ");
 28         }
 29         System.out.println();
 30
 31     }
 32 }

返回目录

命令行参数

咱们再回到Hello World程序:

public class HelloWorld {
    public static void main(String[] args){
        System.out.println("Hello World!");
    }
}

之前只是说你们只要记住main方法的写法,如今要能深刻理解了,“public static void”是什么意思要清楚。咱们这里要说的是main方法的参数 String [] args. args是一字符串数组,它是从哪里来的?Java程序运行时,会调用main方法,args就是命令行参数。

咱们写一个测试程序CommandLine.java:

1 public class CommandLine {
  2     public static void main(String [] args) {
  3         for(String arg : args){
  4             System.out.println(arg);
  5         }
  6     }
  7 }

咱们java CommandLine运行时,没有什么输出。

咱们java CommandLine 1 2 3运行时,输出以下图,此时 args[0]=="1", args[1]=="2", args[2]=="3",args.lenth == 3。

在IDEA这种IDE中如何传递命令行参数?咱们选择Run->Edit Configuration...

命令行中的参数经过 Programm argumetns传递。

实践内容

提交测试结果截图,课下把代码上传到码云。
求命令行传入整数参数的和。
public class CLSum {
    public static void main(String [] args) {

        int sum = 0;

        // 参考Integer类中的方法把字符串转为整数
        // 补充代码求命令行参数的args中整数数据的和
        ...
        
    // 打印 
        System.out.println(sum);
    }
}

参考代码:

public class CLSum {
    public static void main(String [] args) {

        int sum = 0;

        // 参考Integer类中的方法把字符串转为整数
        // 补充代码求命令行参数的args中整数数据的和
        for(String arg: args)
            sum += Interger.parseInt(arg);
        
    // 打印 
        System.out.println(sum);
    }
}

有同窗想先把传入的字符串数组转化为一个临时的int 数组,能够这样:

1 public class CLSum1 {
  2     public static void main(String [] args) {
  3         int sum = 0;
  4
  5         int [] tmp = new int [args.length];
  6         for(int i=0; i<args.length; i++) {
  7             tmp[i] = Integer.parseInt(args[i]);
  8         }
  9
 10         for(int t : tmp){
 11             sum += t;
 12
 13         }
 14
 15         System.out.println(sum);
 16     }
 17 }

同窗们还有遇到Integer类中没有parseInt()的问题,这是咱们实验二定义了本身的Integer等类,与Java重名了,就和两个班有重名的同窗同样,区分必须加上班级名,咱们这就要加上包名java.lang.Integer。

返回目录

递归

递归算法是一种直接或间接地调用自身的算法。在编写程序时,递归算法对解决一大类问题是十分有效的,它每每使算法的描述简洁并且易于理解。

递归用于解决形式相同,规模不一样的问题,能用递归解决的问题均可以转化为循环。递归把一个大型复杂的问题层层转化为一个与原问题类似的规模较小的问题来求解,递归策略只需少许的程序就可描述出解题过程所须要的屡次重复计算,大大地减小了程序的代码量。递归的能力在于用有限的语句来定义对象的无限集合。用递归思想写出的程序每每十分简洁易懂。

递归程序有两个要点:递归公式和结束条件。咱们以求整数的阶乘为例:

有了公式,代码就容易写出来了:

1 public class Factorial {
  2     public static void main(String [] args) {
  3         System.out.println(fact(5));
  4     }
  5
  6     public static int fact(int n) {
  7         if (n == 0)
  8             return 1;
  9         else
 10             return n * fact(n-1);
 11     }
 12 }

fact(5)的递推过程以下图:

JDB不可是个调试工具,仍是一个学习工具,参考 《使用JDB调试Java程序》的递归调试部分,看看递归调用的动态过程。

实践:

public class CLSumRecursion {
    public static void main(String [] args) {

        int sum = 0;

        // 参考Integer类中的方法把字符串转为整数
        // 补充代码以递归的方式求命令行参数的args中整数数据的和
        ...
        
    // 打印 
        System.out.println(sum);
    }
        
        //递归函数
        public static int  clSum(int [] arr) {
           ...
        }
}

参考代码:

1 import java.util.Arrays;
  2
  3 public class CLSumRecursion {
  4     public static void main(String [] args) {
  5         int sum = 0;
  6
  7         if(args.length < 1){
  8             System.out.println("Usage: java CLSumRecursion num1 num2 ...");
  9             System.exit(0);
 10         }
 11
 12         int [] tmp = new int [args.length];
 13         for(int i=0; i<args.length; i++) {
 14             tmp[i] = Integer.parseInt(args[i]);
 15         }
 16
 17         sum =  clsum(tmp);
 18         System.out.println(sum);
 19     }
 20
 21     public static int clsum(int [] arr)
 22         if (arr.length == 1)
 23             return arr[0];
 24         else {
 25             int [] tmp = Arrays.copyOf(arr, arr.length-1);
 26             return clsum(tmp) + arr[arr.length-1];
 27         }
 28     }
 29 }

这个题目出的不太好,用递归求1+2+3+...+N就容易理解些:

int sum(int n){
    if (n==1)
        return 1;
    else
       return sum(n-1) + n;
}

返回目录

分支语句

相对于顺序语句来讲,分支语句多用于处理特殊状况,Java中的分支语句有:

  • if... else if ... else
  • switch...case
    • jdk8中switch 能够是字符串类型
    • 每一个case中不要忘了break

这两个语句是等价的。

使用分支语句时要注意MESE原则,MESE是Mutually Exclusive Collectively Exhaustive的缩写,意思是“相互独立,彻底穷尽”。 也就是对于问题的分类要可以作到不重叠、不遗漏。if中的else, switch...case中的default对于不遗漏.

实践:

实现一个简易计算器Calc,支持+ - x / 和%运算, 从命令行传入计算数据,好比:

java Calc 2 + 3     结果为 2 + 3 = 5
java Calc 8 - 3     结果为 8 - 3 = 5
java Calc 2 x 3     结果为2 x 3 = 6
java Calc 10 / 2     结果为10 / 2 = 5
java Calc 10 % 3     结果为10 % 3 = 1

  
  1 public class Calc {
  2     public static void main(String [] args) {
  3
  4         int result = 0;
  5
  6         if (args.length != 3) {
  7             System.out.println("Usage: java Calc operato1 operand(+ - X / %) operator2");
  8         }
  9         //======如下补充代码=====
 10         //+ - x / 和%运算         
 11         //======以上补充代码======
 12         System.out.println(args[0] + " " + args[1] + " " + args[2] + " = " + result);
 13
 14     }
 15 }

参考代码以下:

1 public class Calc {
  2     public static void main(String [] args) {
  3
  4         int result = 0;
  5
  6         if (args.length != 3) {
  7             System.out.println("Usage: java Calc operato1 operand(+ - x / %) operator2");
  8         }
  9
 10         switch (args[1]) {
 11         case "+":
 12             result = Integer.parseInt(args[0]) + Integer.parseInt(args[2]);
 13             break;
 14         case "-":
 15             result = Integer.parseInt(args[0]) - Integer.parseInt(args[2]);
 16             break;
 17         case "x":
 18             result = Integer.parseInt(args[0]) * Integer.parseInt(args[2]);
 19             break;
 20         case "/":
 21             result = Integer.parseInt(args[0]) / Integer.parseInt(args[2]);
 22             break;
 23         case "%":
 24             result = Integer.parseInt(args[0]) % Integer.parseInt(args[2]);
 25             break;
 26         default:
 27             System.out.println("Usage: java Calc operato1 operand(+ - x / %) operator2");
 28             break;
 29
 30         }
 31         System.out.println(args[0] + " " + args[1] + " " + args[2] + " = " + result);
 32
 33     }
 34 }

这个代码要注意的是乘法在命令行参数中不能用“*”, "*"是一个通配符,会返回当前目录的全部文件名。

还有同窗拷贝上面的代码后要一行一行的删除行号,若是用Vim的话,使用列模式很容易就把删除了,Vim 中咱们使用 Ctrl+v 进入列模式,选择前两列,按x就删除了。

IDEA中也支持Vim模式,熟悉Vim的同窗能够安装IDEAVim插件:

String类的使用

咱们经过String类的使用为例说明程序设计的几个问题

  • 程序设计是用来解决问题的,问题驱动的方式是个好的学习方式
  • 解决问题不是全部问题都要本身解决,能够借助类库,API等经过代码复用来加快开发
  • 要养成写伪代码,产品代码,测试代码的习惯

咱们先回顾一个Linux命令sort:

咱们关注下面几个选项:

-t<分隔字符>:指定排序时所用的栏位分隔字符; 
-k: 针对第几列进行排序
-n:依照数值的大小排序; 
-r:以相反的顺序来排序;

如何实现Linux下Sort的功能对一个字符串数组进行排序?你可能学习过或者据说过冒泡排序,归并排序,插入排序,选择排序,快速排序等算法,咱们要先实现这些算法吗?Java编程中咱们能够先查查API文档:

咱们发现java.util.Arrays类和java.util.Collections类中都实现了sort方法,咱们不用本身编程实现排序算法了,调用这些方法就能够了。

调用java.util.Arrays.sort的示例:

1 import java.util.*;
  2
  3 public class MySort1 {
  4     public static void main(String [] args) {
  5         String [] toSort = {"aaa:10:1:1",
  6                             "ccc:30:3:4",
  7                             "bbb:50:4:5",
  8                             "ddd:20:5:3",
  9                             "eee:40:2:20"};
 10
 11         System.out.println("Before sort:");
 12         for (String str: toSort)
 13                     System.out.println(str);
 14
 15         Arrays.sort(toSort);
 16
 17         System.out.println("After sort:");
 18         for( String str : toSort)
 19             System.out.println(str);
 20     }
 21 }

调用java.util.Collections.sort的示例:

1 import java.util.*;
  2
  3 public class MySort1 {
  4     public static void main(String [] args) {
  5         String [] toSort = {"aaa:10:1:1",
  6                             "ccc:30:3:4",
  7                             "bbb:50:4:5",
  8                             "ddd:20:5:3",
  9                             "eee:40:2:20"};
 10
 11         System.out.println("Before sort:");
 12         for (String str: toSort)
 13                     System.out.println(str);
 14
 15         List<String> list = new ArrayList();
 16         for (String str: toSort)
 17             list.add(str);
 18
 19         Collections.sort(list);
 20
 21         System.out.println("After sort:");
 22         for( String str : list)
 23             System.out.println(str);
 24     }
 25 }

实践任务:

  • 模拟实现Linux下Sort -t : -nk 4的功能,补充第17,25行代码。提交码云连接和代码运行截图。
1 import java.util.*;
  2
  3 public class MySort {
  4     public static void main(String [] args) {
  5         String [] toSort = {"aaa:10:1:1",
  6                             "ccc:30:3:4",
  7                             "bbb:50:4:5",
  8                             "ddd:20:5:3",
  9                             "eee:40:2:20"};
 10
 11         System.out.println("Before sort:");
 12         for (String str: toSort)
 13                     System.out.println(str);
 14
 15         Integer [] tmp = new Integer [toSort.length];
 16         for(int i=0; i<tmp.length; i++)
 17             tmp[i] = ...;
 18
 19         Arrays.sort(tmp);
 20
 21         System.out.println("After sort:");
 22
 23         for(int i=0; i<tmp.length; i++)
 24             for(int j=0; j<toSort.length; j++)
 25                 if(...)
 26                     System.out.println(toSort[j]);
 27     }
 28 }

参考代码

1 import java.util.*;
  2
  3 public class MySort {
  4     public static void main(String [] args) {
  5         String [] toSort = {"aaa:10:1:1",
  6                             "ccc:30:3:4",
  7                             "bbb:50:4:5",
  8                             "ddd:20:5:3",
  9                             "eee:40:2:20"};
 10
 11         System.out.println("Before sort:");
 12         for (String str: toSort)
 13                     System.out.println(str);
 14
 15         Integer [] tmp = new Integer [toSort.length];
 16         for(int i=0; i<tmp.length; i++)
 17             tmp[i] = new Integer(Integer.parseInt(toSort[i].split(":")[3]));
 18
 19         Arrays.sort(tmp);
 20
 21         System.out.println("After sort:");
 22
 23         for(int i=0; i<tmp.length; i++)
 24             for(int j=0; j<toSort.length; j++)
 25                 if(Integer.parseInt(toSort[j].split(":")[3]) == tmp[i].intValue())
 26                     System.out.println(toSort[j]);
 27     }
 28 }

这里面有几个问题:

返回目录

类的定义与测试

返回目录

多态

返回目录

IO与异常

返回目录

数据库

返回目录

网络与安全

返回目录

数据结构应用

实验二 Java面向对象程序设计》提出编写程序要写三种代码:

  • 伪代码
  • 产品代码
  • 测试代码

咱们学习编程是用来解决实际问题的,咱们使用《别出心裁的Linux系统调用学习法》中的方法进行学习》

  • 学习一个Linux命令
  • 分析如何实现,写出伪代码
  • 用Java实现Linux命令,写出产品代码
  • 测试本身的实现,写出测试代码

栈的应用

栈 (Stack)是一种只容许在表尾插入和删除的线性表,有先进后出(FILO),后进先出(LIFO)的特色。容许插入和删除的一端称为栈顶(top),另外一端称为栈底(bottom)。

Java中有Stack类,不熟悉的同窗能够参考《积极主动敲代码,使用Junit学习Java程序设计》学习一下:

  • empty()
  • push()
  • pop()

栈的一个应用是用来对四则运算表达式进行求值。

表达式Exp = S1 + OP + S2(S1 ,S2是两个操做数,OP为运算符)有三种标识方法:

  • OP + S1 + S2 为前缀表示法
  • S1 + OP + S2 为中缀表示法
  • S1 + S2 + OP 为后缀表示法

例如:Exp = a * b + (c - d / e) * f

  • 前缀式: + * a b * - c / d e f
  • 中缀式: a * b + c - d / e * f
  • 后缀式: a b * c d e / - f * +

咱们能够看出:

  1. 操做数之间的相对次序不变;
  2. 运算符的相对次序不一样;
  3. 中缀式丢失了括弧信息,导致运算次序不肯定;
  4. 前缀式的运算规则为:连续出现的两个操做数和在它们以前且紧靠它们的运算符构成一个最小表达式;
  5. 后缀式的运算规则为:运算符在式中出现的顺序恰为表达式的运算顺序;每一个运算符和在它以前出现且紧靠它的两个操做数构成一个最小表达式。

后缀表示法是波兰逻辑学家J.Lukasiewicz于1929年提出的,又叫作逆波兰表达式。

Linux命令dc能够用来对逆波兰式表达式进行求值,dc的打印类命令:

  • p:打印栈顶元素并换行
  • n: 打印栈顶元素并将其弹出栈,完毕后不换行
  • P: putchar ( int(栈顶元素) % 256) 并弹栈顶,不换行
  • f: 从栈顶至栈底打印栈中全部值,每一个一行

dc的运算符:

  • +: 依次弹出w1与w2,将w2+w1压栈。精度为结果值精度
  • -: 依次弹出w1与w2,将w2-w1压栈
  • *: 依次弹出w1与w2,将w2*w1压栈。精度为结果值精度与precision中较大值
  • / : 依次弹出w1与w2,将w2/w1压栈。精度为precision
  • % : 依次弹出w1与w2,将w2-w2/w1*w1压栈
  • ~ : 依次弹出w1与w2,依次将w2/w1与w2%w1压栈
  • ^ : 依次弹出w1与w2,将w2^((int)w1)压栈。精度为w2精度与precision中较大值
  • | : 依次弹出w1 w2与w3,将 w3 ^ ((int)w2) (mod w1) 压栈。w1 w3 需为整数
  • v : 弹出w1,将sqrt(v)压栈。精度为precision

dc支持栈操做:

  • c : 清空栈
  • d : 将栈顶元素复制并压栈
  • r : 交换栈顶两元素 XXX

咱们看一下dc如何使用:

咱们如何实现dc? 这要用到栈。对逆波兰式求值时,不须要再考虑运算符的优先级,只需从左到右扫描一遍后缀表达式便可。求值伪代码以下:

  • 设置一个操做数栈,开始栈为空;
  • 从左到右扫描后缀表达式,遇操做数,进栈;
  • 若遇运算符,则从栈中退出两个元素,先退出的放到运算符的右边,后退出的放到运算符左边,运算后的结果再进栈,直到后缀表达式扫描完毕。

此时,栈中仅有一个元素,即为运算的结果。

咱们给出一个例子,求后缀表达式:1 2 + 8 2 - 7 4 - / * 的值:


MyDC.java: 补充代码31-40行

1 import java.util.StringTokenizer;
  2 import java.util.Stack;
  3
  4 public class MyDC
  5 {
  6   /** constant for addition symbol */
  7   private final char ADD = '+';
  8   /** constant for subtraction symbol */
  9   private final char SUBTRACT = '-';
 10   /** constant for multiplication symbol */
 11   private final char MULTIPLY = '*';
 12   /** constant for division symbol */
 13   private final char DIVIDE = '/';
 14   /** the stack */
 15   private Stack<Integer> stack;
 16
 17   public MyDC() {
 18     stack = new Stack<Integer>();
 19   }
 20
 21   public int evaluate (String expr)
 22   {
 23     int op1, op2, result = 0;
 24     String token;
 25     StringTokenizer tokenizer = new StringTokenizer (expr);
 26
 27     while (tokenizer.hasMoreTokens())
 28     {
 29       token = tokenizer.nextToken();
 30
 31       //若是是运算符,调用isOperator
 32       if ()
 33       {
 34         //从栈中弹出操做数2
 35         //从栈中弹出操做数1
 36         //根据运算符和两个操做数调用evalSingleOp计算result;
 37         //计算result入栈;
 38       }
 39       else//若是是操做数
 40         //操做数入栈;
 41     }
 42
 43     return result;
 44   }
 45
 46   private boolean isOperator (String token)
 47   {
 48     return ( token.equals("+") || token.equals("-") ||
 49              token.equals("*") || token.equals("/") );
 50   }
 51
 52   private int evalSingleOp (char operation, int op1, int op2)
 53   {
 54     int result = 0;
 55
 56     switch (operation)
 57     {
 58       case ADD:
 59         result = op1 + op2;
 60         break;
 61       case SUBTRACT:
 62         result = op1 - op2;
 63         break;
 64       case MULTIPLY:
 65         result = op1 * op2;
 66         break;
 67       case DIVIDE:
 68         result = op1 / op2;
 69     }
 70
 71     return result;
 72   }
 73 }

测试类,MyDCTester.java ,代码不用修改

1 import java.util.Scanner;
  2
  3 public class MyDCTester  {
  4
  5   public static void main (String[] args) {
  6
  7     String expression, again;
  8
  9     int result;
 10
 11     try
 12     {
 13       Scanner in = new Scanner(System.in);
 14
 15       do
 16       {
 17         MyDC evaluator = new MyDC();
 18         System.out.println ("Enter a valid postfix expression: ");
 19         expression = in.nextLine();
 20
 21         result = evaluator.evaluate (expression);
 22         System.out.println();
 23         System.out.println ("That expression equals " + result);
 24
 25         System.out.print ("Evaluate another expression [Y/N]? ");
 26         again = in.nextLine();
 27         System.out.println();
 28       }
 29       while (again.equalsIgnoreCase("y"));
 30     }
 31     catch (Exception IOException)
 32     {
 33       System.out.println("Input exception reported");
 34     }
 35   }
 36 }

参考代码

1 import java.util.StringTokenizer;
  2 import java.util.Stack;
  3
  4 public class MyDC
  5 {
  6   /** constant for addition symbol */
  7   private final char ADD = '+';
  8   /** constant for subtraction symbol */
  9   private final char SUBTRACT = '-';
 10   /** constant for multiplication symbol */
 11   private final char MULTIPLY = '*';
 12   /** constant for division symbol */
 13   private final char DIVIDE = '/';
 14   /** the stack */
 15   private Stack<Integer> stack;
 16
 17   /**
 18    * Sets up this evalutor by creating a new stack.
 19    */
 20   public MyDC()
 21   {
 22     stack = new Stack<Integer>();
 23   }
 24
 25   public int evaluate (String expr)
 26   {
 27     int op1, op2, result = 0;
 28     String token;
 29     StringTokenizer tokenizer = new StringTokenizer (expr);
 30
 31     while (tokenizer.hasMoreTokens())
 32     {
 33       token = tokenizer.nextToken();
 34
 35       if (isOperator(token))
 36       {
 37         op2 = (stack.pop()).intValue();
 38         op1 = (stack.pop()).intValue();
 39         result = evalSingleOp (token.charAt(0), op1, op2);
 40         stack.push (new Integer(result));
 41       }
 42       else
 43         stack.push (new Integer(Integer.parseInt(token)));
 44     }
 45
 46     return result;
 47   }
 48
 49   private boolean isOperator (String token)
 50   {
 51     return ( token.equals("+") || token.equals("-") ||
 52              token.equals("*") || token.equals("/") );
 53   }
 54
 55   private int evalSingleOp (char operation, int op1, int op2)
 56   {
 57     int result = 0;
 58
 59     switch (operation)
 60     {
 61       case ADD:
 62         result = op1 + op2;
 63         break;
 64       case SUBTRACT:
 65         result = op1 - op2;
 66         break;
 67       case MULTIPLY:
 68         result = op1 * op2;
 69         break;
 70       case DIVIDE:
 71         result = op1 / op2;
 72     }
 73
 74     return result;
 75   }
 76 }44     }
 45
 46     return result;
 47   }
 48
 49   private boolean isOperator (String token)
 50   {
 51     return ( token.equals("+") || token.equals("-") ||
 52              token.equals("*") || token.equals("/") );
 53   }
 54
 55   private int evalSingleOp (char operation, int op1, int op2)
 56   {
 57     int result = 0;
 58
 59     switch (operation)
 60     {
 61       case ADD:
 62         result = op1 + op2;
 63         break;
 64       case SUBTRACT:
 65         result = op1 - op2;
 66         break;
 67       case MULTIPLY:
 68         result = op1 * op2;
 69         break;
 70       case DIVIDE:
 71         result = op1 / op2;
 72     }
 73
 74     return result;
 75   }
 76 }

你们注意两个private 方法isOperator, evalSingleOp的设计,体会一下方法分解。

对通常人来讲,获得后缀表达式就是一件不容易的事。咱们习惯的仍是中缀表达式。Linux中另一个计算器bc就是用来计算中缀表达式的:

咱们如何编程实现bc? 把中缀式转化后缀式调用MyDC.java 中的evaluate方法就好了。这样问题转化为如何由中缀式求得后缀式?

由中缀式求得后缀式可使用栈,伪代码以下:

  • 设立一个栈,存放运算符,首先栈为空;
  • 从左到右扫描中缀式,若遇到操做数,直接输出,并输出一个空格做为两个操做数的分隔符;
  • 若遇到运算符,则与栈顶比较,比栈顶级别高则进栈,不然退出栈顶元素并输出,而后输出一个空格做分隔符;
  • 若遇到左括号,进栈;若遇到右括号,则一直退栈输出,直到退到左括号止。
  • 当栈变成空时,输出的结果即为后缀表达式。

将中缀表达式 (1+2)*((8-2)/(7-4)) 变成后缀表达式,栈的变化及输出结果:


算符优先法求解表达式:(生成后缀表达式+后缀表达式求值)

  • 步骤1:创建符号运算的优先级关系表

  • 步骤2
    • (1) 设操做数栈OPND,置空;运算符栈OPTR,最低符号#压进OPTR;
    • (2) 读入字符C,C如果操做数, 进OPND;如果运算符,与OPTR栈顶元素(A)比较,根据算符优先级,决定如何处理:
      • A<C, C压入OPTR栈;
      • A=C, A从OPTR出栈;
      • A>C,A出栈,从OPND依次弹出两个操做数y、x, 计算Z=x A y,Z压入OPND栈。C压进OPTR.
    • (3) 重复(2),直至表达式结束。

返回目录

Android开发

返回目录



若是你以为本文对你有帮助,请点一下左下角的“好文要顶”和“收藏该文

相关文章
相关标签/搜索