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開發

返回目錄



若是你以爲本文對你有幫助,請點一下左下角的「好文要頂」和「收藏該文

相關文章
相關標籤/搜索