圖案列印: 記法與解譯器

來源:互聯網
上載者:User

      童鞋們應該對“列印星形圖案”的編程題記憶猶新吧? 那就像我們的編程童年一樣的美好。 列印邊長為 n 個菱形, HO, 有的人立馬就刷刷刷寫出來了:

      

package patterns.interpretation;public class FigurePrinting {/** * 根據給定 n 值列印菱形圖案 * @param n 菱形的邊數 */public void printDiamond(int n){if (n < 2) {throw new IllegalArgumentException("參數 n = " + n + " 錯誤, 給定參數必須不小於 2!");}int midRow = n-1; for (int row = 0; row <= midRow; row++) {printLine(new int[] { midRow-row , midRow+row}, 2*n-1, '*');}for (int row = midRow+1; row < 2*n-1; row++) { // 對稱性: row = midRow+i 與 row = midRow-i 的圖案是一樣的printLine(new int[] { row - midRow, midRow + (2*midRow - row)}, 2*n-1, '*');}}/** * printLine: 在指定的若干位置上,列印符號; 不列印符號的位置列印空格 * @param indexes 指定要列印符號 symbol 的位置數組 * @param n 一行總共要列印的字元數 * @param symbol 要列印出的符號 */public void printLine(int[] indexes, int n, char symbol){if (indexes == null || indexes.length == 0) { // 如果位置數組為空白,則直接換行System.out.println();return ;}if (n < indexes.length) {throw new IllegalArgumentException("要列印的符號數超過一行的總字元數!");}int k = 0;for (int i=0; i < n; i++) {if (i != indexes[k]) {System.out.printf("%c", ' ');}else {System.out.printf("%c", symbol);k++;if (k == indexes.length) { for (i = indexes[k-1]; i < n; i++) {System.out.printf("%c", ' ');}}}}System.out.println();}}

package patterns.interpretation;public class FigurePrintingTester {private static FigurePrinting fp = new FigurePrinting();public static void testPrintLine(){fp.printLine(null, 3, '*');fp.printLine(new int[0], 3, '*');fp.printLine(new int[] {}, 3, '*');fp.printLine(new int[3] , 3, '*');fp.printLine(new int[] {2,3}, 3, '*');fp.printLine(new int[] { 0, 1, 2 }, 3, '*');}public static void testPrintDiamond(){for (int k = 1; k < 5; k++) {try {System.out.printf("邊長為 %d 的菱形: \n", k);fp.printDiamond(k);} catch (Exception e) {System.out.println(e.getMessage());}}}public static void main(String[] args){testPrintLine();testPrintDiamond();}}

         不過, 上述方式確有一些不足: 

         1.  對需要列印的位置要有精細的控制, 稍一不小心就容易出錯;  2. 幾乎沒有多少可重用性。 一旦要列印更複雜或無規則圖形,則要重新設計控制結構。

          那麼,有沒有其它的解決方案呢?

          有的, 這裡,我們引入記法和解譯器。 所謂記法, 就是對要列印圖案的位置資訊的記錄或者指令; 而解譯器,則對這種記錄或指令進行解析。 比如: 列印符號 H

          X         X

          X   X   X  

          X         X

        可以記做: 1Line 1X 3Blank 1X \n  1Line 1X 1Blank 1X 1Blank 1X \n 1Line 1X 3Blank 1X, 或者簡記為: 1l 1x 3b 1x \n 1l1x1b1x1b1x \n 1l 1x 3b 1x \n. 類似於行程長度編碼。 看上去, 這樣的記法似乎有點繁瑣, 但是,它的靈活性是非常高的, 幾乎對於任意圖形, 都可以寫出這樣的記法, 而對於 1x 1b 1x 1b ... 這樣的常用記法, 可以通過編寫常用常式產生。 

       這樣, 我們所需要的中心任務, 就轉化為確定記法的規則及相應的解譯器。 

       通過對各種圖案的歸納, 可以歸納出描述一行圖案的記法的幾個規則: 

        1.  記法中可以含三種組成成分: (a) [合法數值] [l 或 L] ,  (b) [合法數值] [b 或 B ] , (c) [合法數值] [X 或 x] ; 三種組成成分用多個空格符隔開;

        2.  如果記法中不存在行標記 l 或 L , 則視為 一行 的簡記;  記法中若僅含 l 或 L, 則視為一個或多個空行;

        3.  (a) 總是出現在最前面; 接下來 , (b) 或 (c) 可以任意組合出現或者不出現。

        相應的Regex描述是: 

         [零個或多個空格符] ([合法數值] [lL]) ?  [零個或多個空格符]  ( [零個或多個([合法數值][BbXx][零個或多個空格]) 的組合) [零個或多個空格]. 

         其中, 合法數值是不帶前置字元為零的數值, 即: [1-9] | [1-9][0-9]+

         實現代碼如下: 

         

package patterns.interpretation;import java.util.Scanner;import java.util.regex.Matcher;import java.util.regex.Pattern;public class Interpreter {private static final String PART1_REGEX = "(([1-9]|[1-9][0-9]+)[lL])?";private static final String PART2_REGEX = "((([1-9]|[1-9][0-9]+)[BbXx]\\s*)*)";private static final String LINE_PATTERN_REGEX = "\\s*" + PART1_REGEX + "\\s*" + PART2_REGEX + "\\s*";private static final Pattern LINE_PATTERN = Pattern.compile(LINE_PATTERN_REGEX);private static final Pattern PART_PATTERN = Pattern.compile("([1-9]|[1-9][0-9]+)([bBxX])");/** * interpretFigure: 解釋給定圖形標記,必須由 \n 或 \r 隔開的多個行標記  * @param figureNote */public void interpretFigure(String figureNote){System.out.println("要列印的圖形為: ");for (String lineNote: figureNote.split("\\s*[\r\n]\\s*")) {interpretLine(lineNote, false);}}/** * 解釋給定行的圖形標記 * @param lineNote 給定行的圖形標記 */public void interpretLine(String lineNote, boolean tipFlag){if (tipFlag) {System.out.println("行標記為: " + lineNote);}Matcher matcher = LINE_PATTERN.matcher(lineNote);if (!matcher.matches()) {throw new IllegalArgumentException("不合法的圖形標記,請檢查!");}int lineNum = 0;if (matcher.group(1) == null) {lineNum = 1;} else {    lineNum = Integer.parseInt(matcher.group(2));}String lineInfo = matcher.group(3);for (int i = 0; i < lineNum; i++) {    printLine(lineInfo);    System.out.println();}}/** * 根據給定的行標記資訊列印行 * @param lineInfo 行標記資訊 */public void printLine(String lineInfo){if (lineInfo == null || lineInfo.equals("")) {return ;}String partNote = "";Matcher matcher = PART_PATTERN.matcher("");String flag = null;int num = 0;Scanner scanner = new Scanner(lineInfo);while (scanner.hasNext(PART_PATTERN)) {partNote = scanner.next();    matcher.reset(partNote);    if (matcher.matches()) {num = Integer.parseInt(matcher.group(1));flag = matcher.group(2);if (flag.equals("x") || flag.equals("X")) {printNChars('x', num);}else {printNChars(' ', num);}    }}}/* * 列印 n 個 由 ch 指定的符號 */private void printNChars(char ch, int n){if (n <= 0 ) { return ; }while (n-- > 0) {System.out.printf("%c", ch);}}}

package patterns.interpretation;public class InterpreterTester {private static Interpreter ip = new Interpreter();public static void main(String[] args){testInterpretLine();printDiamond();printCharH();printCharC();}public static void printDiamond(){String figureNote = "2b 1x 2b \n 1b 1x 1b 1x 1b \n 1x 3b 1x \n 1b 1x 1b 1x 1b \n 2b 1x 2b \n";ip.interpretFigure(figureNote);}public static void printCharH(){String figureNote = "2l 1x 3b 1x \n 5x \n 2l 1x 3b 1x\n";ip.interpretFigure(figureNote);}public static void printCharC(){String figureNote = "1b 5x \n 3l 1x \n 1b 5x \n";ip.interpretFigure(figureNote);}public static void testInterpretLine(){String exString1 = "3l \n 3L \n 1l 4b \n 1l 3x \n 3l 2b 2x \n ";String exString2 = "3l 5b 2x 3b\n  3l 5x 3b 5x \n";String exString3 = "03l 23x 4b \n 3l 023x 43b \n 3l 23x 043b \n 5b  \n 3x \n  5b 3x \n 3x 5b \n";String totalString = exString1 + exString2 + exString3;String[] multLines = totalString.split("\n");for (String line: multLines) {try {   ip.interpretLine(line, true);} catch (Exception e) {   System.out.println(e.getMessage());}                 }}}

        總結:

        通過引入一套簡潔的記法和相應的解譯器, 我們提高了列印圖案的重用性。 對於任意要列印的圖案, 只要遵循這種記法寫出相應的指令, 就可以輕易地列印出期望的圖案。 並且, 這種記法對於非技術性普通使用者也是易於理解的。 這說明了, 記法與解譯器, 確實一種另闢蹊徑的求解方案。

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.