RosettaCodeData/Task/Floyds-triangle/JavaScript/floyds-triangle-2.js

98 lines
2.9 KiB
JavaScript

(() => {
'use strict';
// FLOYD's TRIANGLE -------------------------------------------------------
// floyd :: Int -> [[Int]]
const floyd = n => snd(mapAccumL(
(start, row) => [start + row + 1, enumFromTo(start, start + row)],
1, enumFromTo(0, n - 1)
));
// showFloyd :: [[Int]] -> String
const showFloyd = xss => {
const ws = map(compose([succ, length, show]), last(xss));
return unlines(
map(xs => concat(zipWith(
(w, x) => justifyRight(w, ' ', show(x)), ws, xs
)),
xss
)
);
};
// GENERIC FUNCTIONS ------------------------------------------------------
// compose :: [(a -> a)] -> (a -> a)
const compose = fs => x => fs.reduceRight((a, f) => f(a), x);
// concat :: [[a]] -> [a] | [String] -> String
const concat = xs => {
if (xs.length > 0) {
const unit = typeof xs[0] === 'string' ? '' : [];
return unit.concat.apply(unit, xs);
} else return [];
};
// enumFromTo :: Int -> Int -> [Int]
const enumFromTo = (m, n) =>
Array.from({
length: Math.floor(n - m) + 1
}, (_, i) => m + i);
// justifyRight :: Int -> Char -> Text -> Text
const justifyRight = (n, cFiller, strText) =>
n > strText.length ? (
(cFiller.repeat(n) + strText)
.slice(-n)
) : strText;
// last :: [a] -> a
const last = xs => xs.length ? xs.slice(-1)[0] : undefined;
// length :: [a] -> Int
const length = xs => xs.length;
// 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, []]);
// show ::
// (a -> String) f, Num n =>
// a -> maybe f -> maybe n -> String
const show = JSON.stringify;
// snd :: (a, b) -> b
const snd = tpl => Array.isArray(tpl) ? tpl[1] : undefined;
// succ :: Int -> Int
const succ = x => x + 1
// unlines :: [String] -> String
const unlines = xs => xs.join('\n');
// zipWith :: (a -> b -> c) -> [a] -> [b] -> [c]
const zipWith = (f, xs, ys) => {
const ny = ys.length;
return (xs.length <= ny ? xs : xs.slice(0, ny))
.map((x, i) => f(x, ys[i]));
};
// TEST ( n=5 and n=14 rows ) ---------------------------------------------
return unlines(map(n => showFloyd(floyd(n)) + '\n', [5, 14]))
})();