RosettaCodeData/Task/Pascals-triangle/JavaScript/pascals-triangle-4.js

121 lines
3.1 KiB
JavaScript

(() => {
'use strict';
const main = () =>
showPascal(take(7, pascal()));
// pascal :: Generator [[Int]]
const pascal = () =>
iterate(
xs => zipWith(
plus,
append([0], xs), append(xs, [0])
),
[1]
);
// showPascal :: [[Int]] -> String
const showPascal = xs => {
const
w = length(intercalate(' ', last(xs))),
align = xs => center(w, ' ', intercalate(' ', xs));
return unlines(map(align, xs));
};
// GENERIC FUNCTIONS ----------------------------------
// Tuple (,) :: a -> b -> (a, b)
const Tuple = (a, b) => ({
type: 'Tuple',
'0': a,
'1': b,
length: 2
});
// append (++) :: [a] -> [a] -> [a]
// append (++) :: String -> String -> String
const append = (xs, ys) => xs.concat(ys);
// Size of space -> filler Char -> String -> Centered String
// center :: Int -> Char -> String -> String
const center = (n, c, s) => {
const
qr = quotRem(n - s.length, 2),
q = qr[0];
return replicateString(q, c) +
s + replicateString(q + qr[1], c);
};
// intercalate :: String -> [String] -> String
const intercalate = (s, xs) =>
xs.join(s);
// iterate :: (a -> a) -> a -> Generator [a]
function* iterate(f, x) {
let v = x;
while (true) {
yield(v);
v = f(v);
}
}
// last :: [a] -> a
const last = xs =>
0 < xs.length ? xs.slice(-1)[0] : undefined;
// Returns Infinity over objects without finite length
// this enables zip and zipWith to choose the shorter
// argument when one non-finite like cycle, repeat etc
// length :: [a] -> Int
const length = xs => xs.length || Infinity;
// map :: (a -> b) -> [a] -> [b]
const map = (f, xs) => xs.map(f);
// plus :: Num -> Num -> Num
const plus = (a, b) => a + b;
// quotRem :: Int -> Int -> (Int, Int)
const quotRem = (m, n) =>
Tuple(Math.floor(m / n), m % n);
// replicateString :: Int -> String -> String
const replicateString = (n, s) => s.repeat(n);
// take :: Int -> [a] -> [a]
// take :: Int -> String -> String
const take = (n, xs) =>
xs.constructor.constructor.name !== 'GeneratorFunction' ? (
xs.slice(0, n)
) : [].concat.apply([], Array.from({
length: n
}, () => {
const x = xs.next();
return x.done ? [] : [x.value];
}));
// unlines :: [String] -> String
const unlines = xs => xs.join('\n');
// Use of `take` and `length` here allows zipping with non-finite lists
// i.e. generators like cycle, repeat, iterate.
// zipWith :: (a -> b -> c) -> [a] -> [b] -> [c]
const zipWith = (f, xs, ys) => {
const
lng = Math.min(length(xs), length(ys)),
as = take(lng, xs),
bs = take(lng, ys);
return Array.from({
length: lng
}, (_, i) => f(as[i], bs[i], i));
};
// MAIN ---
return main();
})();