程式中可能會發生的錯誤有三種,分別是語法錯誤 (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
沒有留言:
張貼留言