
程式中可能會發生的錯誤有三種,分別是語法錯誤 (syntax error) 、執行期間錯誤 (run-time error) 及語意錯誤 (semantic error)

編譯器 (compiler) 會直接幫我們挑出語法錯誤,例如打錯識別字 (identifier) 名稱或是漏打分號等等。執行期間錯誤的話, Java 另有例外處理 (exception handling) 的機制,讓我們可以處理開啟指定檔名,而檔案不存在的情況。三種錯誤中最麻煩的,就是語意錯誤了,因為有語意錯誤的程式,程式可以順利執行完畢,卻跑出錯誤的結果。
我們的 Encrypt 類別 (class) 目前正是碰到了發生語意錯誤的情況,這是說
y = a * x + b
m = y % n
r = m + diff
其中 a 與 b 若是 0 到 9 隨機整數,有些組合成立,可以得到正確結果,有些組合卻會得到錯誤的結果,這是為什麼呢?嗯,好麻煩唷!這樣就得討論好多數學,打斷我們發展程式的腳步,所以我們不打算仔細討論這背後的數學理論,我們繼續測試,直接來找出哪些組合會得到錯誤的結果吧!
要知道哪些組合可能會發生錯誤,我們就得知道 a 與 b 的值,這不難,印出來就看得到了。我們把 Encrypt03 加入印出 a 、 b 值的程式碼,修改成 Encrypt04 ,如下
| 001 | public class Encrypt04 { |
| 002 | // 密碼表字元陣列 |
| 003 | private char[] code = new char[26]; |
| 004 | |
| 005 | // 建構子 |
| 006 | public Encrypt04() { |
| 007 | setCode(); |
| 008 | } |
| 009 | |
| 010 | // setter |
| 011 | public void setCode() { |
| 012 | int a = 0; |
| 013 | int b = 0; |
| 014 | |
| 015 | a = (int) (Math.random() * 10); |
| 016 | System.out.printf("%d, ", a); |
| 017 | b = (int) (Math.random() * 10); |
| 018 | System.out.printf("%d, ", b); |
| 019 | |
| 020 | int x, y, m, i; |
| 021 | char c = 'a'; |
| 022 | for (i = 0; i < 26; i++) { |
| 023 | x = c; |
| 024 | y = x * a + b; |
| 025 | m = y % 26; |
| 026 | code[i] = (char) (m + 97); |
| 027 | c++; |
| 028 | } |
| 029 | } |
| 030 | |
| 031 | // getter |
| 032 | public char[] getCode() { |
| 033 | return code; |
| 034 | } |
| 035 | |
| 036 | // 編碼的方法 |
| 037 | public String toEncode(String s) { |
| 038 | return s; |
| 039 | } |
| 040 | |
| 041 | // 解碼的方法 |
| 042 | public String toDecode(String s) { |
| 043 | return s; |
| 044 | } |
| 045 | |
| 046 | // 測試的 main() |
| 047 | public static void main(String[] args) { |
| 048 | for (int i = 0; i < 16; i++) { |
| 049 | Encrypt04 e = new Encrypt04(); |
| 050 | System.out.println(e.getCode()); |
| 051 | } |
| 052 | //String s = "There is no spoon"; |
| 053 | //System.out.println(s); |
| 054 | //String s1 = e.toEncode(s); |
| 055 | //System.out.println(s1); |
| 056 | //String s2 = e.toDecode(s1); |
| 057 | //System.out.println(s2); |
| 058 | } |
| 059 | } |
| 060 | |
| 061 | /* 檔名: Encrypt04.java |
| 062 | 作者: Kaiching Chang |
| 063 | 時間: September, 2014 */ |
我們在 setCode() 中用 printf() 印出 a 、 b 值, printf() 預設不會印出新行符號,因此印出結果會是 a 、 b 值加上密碼表一行
| 015 | a = (int) (Math.random() * 10); |
| 016 | System.out.printf("%d, ", a); |
| 017 | b = (int) (Math.random() * 10); |
| 018 | System.out.printf("%d, ", b); |
來編譯測試看看囉

結果顯示 a 為偶數或 0 就會跑出不符預期的結果,那我們就把 a 改成不是偶數或 0 好了!公式修改如下
if (a % 2) != 0 {
y = a * x + b
m = y % n
r = m + diff
}
這樣我們把發展中的版本 Encrypt04 修改為 Encrypt05 ,其中需要修改的部份只有 setCode() ,如下
| 010 | // setter |
| 011 | public void setCode() { |
| 012 | int a = 0; |
| 013 | int b = 0; |
| 014 | |
| 015 | while (a % 2 == 0) { |
| 016 | a = (int) (Math.random() * 10); |
| 017 | b = (int) (Math.random() * 10); |
| 018 | } |
| 019 | System.out.printf("%d, ", a); |
| 020 | System.out.printf("%d, ", b); |
| 021 | |
| 022 | int x, y, m, i; |
| 023 | char c = 'a'; |
| 024 | for (i = 0; i < 26; i++) { |
| 025 | x = c; |
| 026 | y = x * a + b; |
| 027 | m = y % 26; |
| 028 | code[i] = (char) (m + 97); |
| 029 | c++; |
| 030 | } |
| 031 | } |
我們先把 a 與 b 的初值設為 0 ,然後以迴圈 (loop) 取得隨機的 a 、 b 值,用 a 除以 2 的餘數為 0 當作迴圈的執行條件,因此當 a 等於 0 或偶數時,迴圈就會持續進行,直到 a 不為 0 或偶數為止
| 012 | int a = 0; |
| 013 | int b = 0; |
| 014 | |
| 015 | while (a % 2 == 0) { |
| 016 | a = (int) (Math.random() * 10); |
| 017 | b = (int) (Math.random() * 10); |
| 018 | } |
| 019 | System.out.printf("%d, ", a); |
| 020 | System.out.printf("%d, ", b); |
| 021 |
重新編譯執行,結果如下

肉眼檢查下,似乎只要 a 為奇數,計算出的結果就不會有問題囉!
下面我們要開始實作處理編碼及解碼的部分,也就是實作 toEncode() 與 toDncode() 方法 (method) 。
中英文術語對照
| 語法錯誤 | syntax error |
| 執行期間錯誤 | run-time error |
| 語意錯誤 | semantic error |
| 編譯器 | compiler |
| 識別字 | identifier |
| 例外處理 | exception handling |
| 類別 | class |
| 迴圈 | loop |
| 方法 | method |
重點整理
- 程式中可能會發生的錯誤有三種,分別是語法錯誤、執行期間錯誤及語意錯誤。
- 語意錯誤是程式順利執行,卻跑出非預期結果的情況。
- 公式中的 a 、 b 值在 a 為偶數的時候出錯。
問題與討論
- 為什麼編譯器會直接挑出語法錯誤?
- 什麼是執行期間錯誤?為什麼會發生執行期間錯誤?
- 什麼是語意錯誤?為什麼發生語意錯誤的程式還能順利執行?
the end
沒有留言:
張貼留言