66 lines
2.0 KiB
JavaScript
66 lines
2.0 KiB
JavaScript
(() => {
|
|
// ROMAN INTEGER STRINGS ----------------------------------------------------
|
|
|
|
// roman :: Int -> String
|
|
const roman = n =>
|
|
concat(snd(mapAccumL((balance, [k, v]) => {
|
|
const [q, r] = quotRem(balance, v);
|
|
return [r, q > 0 ? k.repeat(q) : ''];
|
|
}, n, [
|
|
['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]
|
|
])));
|
|
|
|
// GENERIC FUNCTIONS -------------------------------------------------------
|
|
|
|
// concat :: [[a]] -> [a] | [String] -> String
|
|
const concat = xs =>
|
|
xs.length > 0 ? (() => {
|
|
const unit = typeof xs[0] === 'string' ? '' : [];
|
|
return unit.concat.apply(unit, xs);
|
|
})() : [];
|
|
|
|
// map :: (a -> b) -> [a] -> [b]
|
|
const map = (f, xs) => xs.map(f);
|
|
|
|
// 'The mapAccumL function behaves like a combination of map and foldl;
|
|
// it applies a function to each element of a list, passing an accumulating
|
|
// parameter from left to right, and returning a final value of this
|
|
// accumulator together with the new list.' (See Hoogle)
|
|
|
|
// mapAccumL :: (acc -> x -> (acc, y)) -> acc -> [x] -> (acc, [y])
|
|
const mapAccumL = (f, acc, xs) =>
|
|
xs.reduce((a, x) => {
|
|
const pair = f(a[0], x);
|
|
return [pair[0], a[1].concat([pair[1]])];
|
|
}, [acc, []]);
|
|
|
|
// quotRem :: Integral a => a -> a -> (a, a)
|
|
const quotRem = (m, n) => [Math.floor(m / n), m % n];
|
|
|
|
// show :: a -> String
|
|
const show = (...x) =>
|
|
JSON.stringify.apply(
|
|
null, x.length > 1 ? [x[0], null, x[1]] : x
|
|
);
|
|
|
|
// snd :: (a, b) -> b
|
|
const snd = tpl => Array.isArray(tpl) ? tpl[1] : undefined;
|
|
|
|
// TEST -------------------------------------------------------------------
|
|
return show(
|
|
map(roman, [2016, 1990, 2008, 2000, 1666])
|
|
);
|
|
})();
|