79 lines
2.0 KiB
JavaScript
79 lines
2.0 KiB
JavaScript
(() => {
|
|
"use strict";
|
|
|
|
// -------------- ROMAN INTEGER STRINGS --------------
|
|
|
|
// roman :: Int -> String
|
|
const roman = n =>
|
|
mapAccumL(residue =>
|
|
([k, v]) => second(
|
|
q => 0 < q ? (
|
|
k.repeat(q)
|
|
) : ""
|
|
)(remQuot(residue)(v))
|
|
)(n)(
|
|
zip([
|
|
"M", "CM", "D", "CD", "C", "XC",
|
|
"L", "XL", "X", "IX", "V", "IV", "I"
|
|
])([
|
|
1000, 900, 500, 400, 100, 90,
|
|
50, 40, 10, 9, 5, 4, 1
|
|
])
|
|
)[1]
|
|
.join("");
|
|
|
|
|
|
// ---------------------- TEST -----------------------
|
|
// main :: IO ()
|
|
const main = () => (
|
|
[2016, 1990, 2008, 2000, 2020, 1666].map(roman)
|
|
).join("\n");
|
|
|
|
|
|
// ---------------- GENERIC FUNCTIONS ----------------
|
|
|
|
// mapAccumL :: (acc -> x -> (acc, y)) -> acc ->
|
|
// [x] -> (acc, [y])
|
|
const mapAccumL = f =>
|
|
// A tuple of an accumulation and a list
|
|
// obtained by a combined map and fold,
|
|
// with accumulation from left to right.
|
|
acc => xs => [...xs].reduce(
|
|
(a, x) => {
|
|
const tpl = f(a[0])(x);
|
|
|
|
return [
|
|
tpl[0],
|
|
a[1].concat(tpl[1])
|
|
];
|
|
},
|
|
[acc, []]
|
|
);
|
|
|
|
|
|
// remQuot :: Int -> Int -> (Int, Int)
|
|
const remQuot = m =>
|
|
n => [m % n, Math.trunc(m / n)];
|
|
|
|
|
|
// second :: (a -> b) -> ((c, a) -> (c, b))
|
|
const second = f =>
|
|
// A function over a simple value lifted
|
|
// to a function over a tuple.
|
|
// f (a, b) -> (a, f(b))
|
|
xy => [xy[0], f(xy[1])];
|
|
|
|
|
|
// zip :: [a] -> [b] -> [(a, b)]
|
|
const zip = xs =>
|
|
// The paired members of xs and ys, up to
|
|
// the length of the shorter of the two lists.
|
|
ys => Array.from({
|
|
length: Math.min(xs.length, ys.length)
|
|
}, (_, i) => [xs[i], ys[i]]);
|
|
|
|
|
|
// MAIN --
|
|
return main();
|
|
})();
|