基於遞歸降低的羅馬數字Parser

羅馬數字生成規則:java

羅馬數字共有7個,即Ⅰ(1)、Ⅴ(5)、Ⅹ(10)、Ⅼ(50)、Ⅽ(100)、Ⅾ(500)和Ⅿ(1000)。按照下述的規則能夠表示任意正整數。須要注意的是羅馬數字中沒有「0」,與進位制無關。通常認爲羅馬數字只用來記數,而不做演算。git

  • 重複數次:一個羅馬數字重複幾回,就表示這個數的幾倍。
  • 右加左減:
    • 在較大的羅馬數字的右邊記上較小的羅馬數字,表示大數字加小數字。
    • 在較大的羅馬數字的左邊記上較小的羅馬數字,表示大數字減少數字。
    • 左減的數字有限制,僅限於I、X、C。好比45不能夠寫成VL,只能是XLV
    • 可是,左減時不可跨越一個位值。好比,99不能夠用IC({\displaystyle 100-1}100-1)表示,而是用XCIX({\displaystyle [100-10]+[10-1]}[100-10]+[10-1])表示。(等同於阿拉伯數字每位數字分別表示。)
    • 左減數字必須爲一位,好比8寫成VIII,而非IIX。
    • 右加數字不可連續超過三位,好比14寫成XIV,而非XIIII。(見下方「數碼限制」一項。)
  • 加線乘千:
    • 在羅馬數字的上方加上一條橫線或者加上下標的Ⅿ,表示將這個數乘以1000,便是原數的1000倍。
    • 同理,若是上方有兩條橫線,便是原數的1000000({\displaystyle 1000^{2}}1000^{{2}})倍。
  • 數碼限制:
    • 同一數碼最多隻能連續出現三次,如40不可表示爲XXXX,而要表示爲XL。
    • 例外:因爲IV是古羅馬神話主神朱庇特(即IVPITER,古羅馬字母裏沒有J和U)的首字,所以有時用IIII代替IV。

基於生成規則的文法:this

* Grammar:
* Thous -> M|MM|MMM|e<br>
* 1e300 -> C|CC|CCC|e<br>
* Hunds -> 1e300|CD|D1e300|CM|e<br>
* 1e30 -> X|XX|XXX|e<br>
* Tens -> 1e30|XL|L1e30|XC|e<br>
* 1e3 -> I|II|III|e<br>
* Units -> 1e3|IV|V1e3|IX|e<br>
* <p>
* Number -> ThousHundsTensUnits
* <p>
* e:empty

完整的java程序代碼:code

public final class RomanNumberParser {


    private final char[] chars;
    private int lookahead;
    private int arabic;

    public RomanNumberParser(String romanNumber) {
        if (romanNumber == null || romanNumber.length() == 0)
            throw new IllegalArgumentException("IllegalNumber");
        this.chars = romanNumber.toCharArray();
        this.lookahead = 0;
        this.arabic = 0;
    }

    public static void main(String[] args) {
        System.out.println(new RomanNumberParser("MDCCCLXXX").parse());
    }

    private void match(char c) {
        if (chars[lookahead] == c) {
            lookahead++;
        } else {
            throw new IllegalStateException();
        }
    }

    private void thous() {
        for (int i = 0; i < 3; i++) {
            if (lookahead('M')) {
                match('M');
                arabic += 1000;
            } else break;
        }
    }

    private void hundreds() {
        digit('C', 'D', 'M', 100);
    }

    private void tens() {
        digit('X', 'L', 'C', 10);
    }

    private void units() {
        digit('I', 'V', 'X', 1);
    }

    private boolean lookahead(char c) {
        return lookahead < chars.length && chars[lookahead] == c;
    }

    private void bis(char c, int nc) {
        match(c);
        arabic += nc;
        if (lookahead(c))
            bis(c, nc);
    }

    private void digit(char c1, char c5, char c10, int nc1) {
        if (lookahead(c1)) {
            match(c1);
            if (lookahead(c1)) {
                arabic += nc1;
                bis(c1, nc1);
            } else if (lookahead(c5)) {
                match(c5);
                arabic += 4 * nc1;
            } else if (lookahead(c10)) {
                match(c10);
                arabic += 9 * nc1;
            } else {
                    arabic += nc1;
             }
        } else if (lookahead(c5)) {
            match(c5);
            arabic += 5 * nc1;
            for (int i = 0; i < 3; i++) {
                if (lookahead(c1)) {
                    match(c1);
                    arabic += nc1;
                }
            }
        }
    }

    public int parse() {
        thous();
        hundreds();
        tens();
        units();
        return arabic;
    }

}
相關文章
相關標籤/搜索