|
1 abstract class RomanDigit |
|
2 case object I extends RomanDigit |
|
3 case object V extends RomanDigit |
|
4 case object X extends RomanDigit |
|
5 case object L extends RomanDigit |
|
6 case object C extends RomanDigit |
|
7 case object D extends RomanDigit |
|
8 case object M extends RomanDigit |
|
9 |
|
10 type RomanNumeral = List[RomanDigit] |
|
11 |
|
12 def optionlist[A](xs: List[Option[A]]): Option[List[A]] = xs match { |
|
13 case Nil => Some(Nil) |
|
14 case x::xs => (x, optionlist(xs)) match { |
|
15 case (None, _) => None |
|
16 case (_, None) => None |
|
17 case (Some(y), Some(ys)) => Some(y::ys) |
|
18 } |
|
19 } |
|
20 |
|
21 def char2romandigit(c: Char): Option[RomanDigit] = c match { |
|
22 case 'I' => Some(I) |
|
23 case 'V' => Some(V) |
|
24 case 'X' => Some(X) |
|
25 case 'L' => Some(L) |
|
26 case 'C' => Some(C) |
|
27 case 'D' => Some(D) |
|
28 case 'M' => Some(M) |
|
29 case _ => None |
|
30 } |
|
31 |
|
32 def String2RomanNumeral(s: String) : Option[RomanNumeral] = |
|
33 optionlist(s.toList.map(char2romandigit)) |
|
34 |
|
35 String2RomanNumeral("IIII") |
|
36 String2RomanNumeral("IV") |
|
37 String2RomanNumeral("VI") |
|
38 String2RomanNumeral("IX") |
|
39 String2RomanNumeral("MCMLXXIX") |
|
40 String2RomanNumeral("MCMXLIV") |
|
41 String2RomanNumeral("M C M X L I V") // None |
|
42 |
|
43 //////////////// |
|
44 |
|
45 def RomanNumeral2Int(rs: RomanNumeral): Int = rs match { |
|
46 case Nil => 0 |
|
47 case M::r => 1000 + RomanNumeral2Int(r) |
|
48 case C::M::r => 900 + RomanNumeral2Int(r) |
|
49 case D::r => 500 + RomanNumeral2Int(r) |
|
50 case C::D::r => 400 + RomanNumeral2Int(r) |
|
51 case C::r => 100 + RomanNumeral2Int(r) |
|
52 case X::C::r => 90 + RomanNumeral2Int(r) |
|
53 case L::r => 50 + RomanNumeral2Int(r) |
|
54 case X::L::r => 40 + RomanNumeral2Int(r) |
|
55 case X::r => 10 + RomanNumeral2Int(r) |
|
56 case I::X::r => 9 + RomanNumeral2Int(r) |
|
57 case V::r => 5 + RomanNumeral2Int(r) |
|
58 case I::V::r => 4 + RomanNumeral2Int(r) |
|
59 case I::r => 1 + RomanNumeral2Int(r) |
|
60 } |
|
61 |
|
62 RomanNumeral2Int(List(I,I,I,I)) // 4 |
|
63 RomanNumeral2Int(List(I,V)) // 4 |
|
64 RomanNumeral2Int(List(V,I)) // 6 |
|
65 RomanNumeral2Int(List(I,X)) // 9 |
|
66 RomanNumeral2Int(List(M,C,M,L,X,X,I,X)) // 1979 |
|
67 RomanNumeral2Int(List(M,C,M,X,L,I,V)) // 1944 |
|
68 |
|
69 |
|
70 def String2Int(s: String): Option[Int] = |
|
71 String2RomanNumeral(s).map(RomanNumeral2Int(_)) |
|
72 |
|
73 |
|
74 String2Int("IIII") // 4 invalid |
|
75 String2Int("IV") // 4 |
|
76 String2Int("VI") // 6 |
|
77 String2Int("IX") // 9 |
|
78 String2Int("MCMLXXIX") // 1979 |
|
79 String2Int("MCMXLIV") // 1944 |
|
80 String2Int("") // 0 |
|
81 String2Int("MDCLXI") // 1661 |
|
82 String2Int("MMMCMXCIX") // 3999 |
|
83 String2Int("XLVIII") // 48 |
|
84 String2Int("MMVIII") // 2008 |
|
85 |
|
86 String2Int("MMXI") // 2011 |
|
87 String2Int("MIM") // 1999 |
|
88 String2Int("MCMLVI") // 1956 |
|
89 String2Int("XXCIII") // ?? 103 / 83 |
|
90 String2Int("LXXIIX") // ?? 80 / 78 |
|
91 String2Int("IIIIX") // ?? 12 invalid |
|
92 String2Int("IIXX") // ?? 20 / 18 invalid |
|
93 |
|
94 String2Int("III") // 3 |
|
95 String2Int("XXX") // 30 |
|
96 String2Int("CCC") // 300 |
|
97 String2Int("MMM") // 3000 |
|
98 String2Int("VII") // 7 |
|
99 String2Int("LXVI") // 66 |
|
100 String2Int("CL") // 150 |
|
101 String2Int("MCC") // 1200 |
|
102 String2Int("IV") // 4 |
|
103 String2Int("IX") // 9 |
|
104 String2Int("XC") // 90 |
|
105 String2Int("ICM") // ?? 901 |
|
106 String2Int("CIM") // ?? 899 |
|
107 String2Int("MDCLXVI") // 1666 |
|
108 |
|
109 String2Int("VIV") //9, but should be written as IX |
|
110 String2Int("IVX") // 14 invalid |
|
111 |
|
112 // error cases |
|
113 String2Int("MC?I") |
|
114 String2Int("abc") |
|
115 |
|
116 |
|
117 def runsAllowed(r: RomanDigit) = r match { |
|
118 case I | X | C | M => true |
|
119 case V | L | D => false |
|
120 } |
|
121 |
|
122 def norunsAllowed(r: RomanDigit) = !runsAllowed(r) |
|
123 |
|
124 |
|
125 def isValidNumeral(digitList: RomanNumeral): Bool = digitList match { |
|
126 |
|
127 // empty list is valid |
|
128 case Nil => true |
|
129 |
|
130 // a following digit that is equal or larger is an error |
|
131 case d1::d2::_ if (d1 <= d2) => false |
|
132 |
|
133 // A single digit is always allowed |
|
134 case _::ds => isValidNumeral(ds) |
|
135 |
|
136 } |