-
Notifications
You must be signed in to change notification settings - Fork 1
/
p89.janet
83 lines (75 loc) · 2.14 KB
/
p89.janet
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
(def roman-numerals
"Roman numeral sequences, descending"
[["M" 1000]
["CM" 900]
["D" 500]
["CD" 400]
["C" 100]
["XC" 90]
["L" 50]
["XL" 40]
["X" 10]
["IX" 9]
["V" 5]
["IV" 4]
["I" 1]])
(defn number->roman-numerals
"Format number as minimal Roman numerals by appending strings in descending order"
[num]
(def buffer (buffer))
(var n num)
(each [s value] roman-numerals
(while (>= n value)
(buffer/push-string buffer s)
(-= n value)))
(string buffer))
# Note: Using a Parsing Expression Grammar for this problem is overkill since all provided inputs are valid (and a
# regular expression would probably suffice), but I wanted to give Janet's built-in PEG support a try.
(defn roman-numeral-to-rule
"Convert a Roman numeral string and value to a grammar rule"
[[str value]]
[(keyword (string/ascii-lower str))
~(sequence ,str (constant ,value))])
(def grammar
"Grammar for parsing Roman numerals"
(peg/compile
# There must be some easier way to splice into a struct...
(struct (splice
~[,;(mapcat roman-numeral-to-rule roman-numerals)
:main (sequence
(any :m)
(? :cm)
(? (choice
:d
:cd))
(any :c)
(? :xc)
(? (choice
:l
:xl))
(any :x)
(? :ix)
(? (choice
:v
:iv))
(any :i))]))))
(defn roman-numerals->number
"Parse Roman numerals using grammar and sum values"
[string]
(->> string
(peg/match grammar)
(reduce + 0)))
(defn diff
"Calculate the difference in characters of original vs. minimal form"
[numerals]
(- (length numerals)
(-> numerals
(roman-numerals->number)
(number->roman-numerals)
(length))))
# Entry point
(pp (->> "0089_roman.txt"
(slurp)
(string/split "\n")
(map diff)
(reduce + 0)))