基于递归降低的罗马数字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;
    }

}
相关文章
相关标签/搜索