RosettaCodeData/Task/Gapful-numbers/JavaScript/gapful-numbers-2.js

209 lines
5.1 KiB
JavaScript

(() => {
'use strict';
// ------------------ GAPFUL NUMBERS -------------------
// isGapful :: Int -> Bool
const isGapful = n =>
compose(
x => 0 === n % x,
JSON.parse,
concat,
ap([head, last]),
x => [x],
JSON.stringify
)(n);
// ----------------------- TEST ------------------------
const main = () =>
unlines([
'First 30 gapful numbers >= 100',
'First 15 Gapful numbers >= 1E6',
'First 10 Gapful numbers >= 1E9'
].map(k => {
const
ws = words(k),
mn = [1, 5].map(
i => JSON.parse(ws[i])
);
return k + ':\n\n' + showList(
take(mn[0])(
filterGen(isGapful)(
enumFrom(mn[1])
)
)
);
}));
// ----------------- GENERIC FUNCTIONS -----------------
// ap (<*>) :: [(a -> b)] -> [a] -> [b]
const ap = fs =>
// The sequential application of each of a list
// of functions to each of a list of values.
xs => fs.flatMap(f => xs.map(x => f(x)));
// chunksOf :: Int -> [a] -> [[a]]
const chunksOf = n =>
xs => enumFromThenTo(0)(n)(
xs.length - 1
).reduce(
(a, i) => a.concat([xs.slice(i, (n + i))]),
[]
);
// compose (<<<) :: (b -> c) -> (a -> b) -> a -> c
const compose = (...fs) =>
// A function defined by the right-to-left
// composition of all the functions in fs.
fs.reduce(
(f, g) => x => f(g(x)),
x => x
);
// concat :: [[a]] -> [a]
// concat :: [String] -> String
const concat = xs => (
ys => 0 < ys.length ? (
ys.every(Array.isArray) ? (
[]
) : ''
).concat(...ys) : ys
)(list(xs));
// enumFrom :: Enum a => a -> [a]
function* enumFrom(x) {
// A non-finite succession of enumerable
// values, starting with the value x.
let v = x;
while (true) {
yield v;
v = succ(v);
}
}
// enumFromThenTo :: Int -> Int -> Int -> [Int]
const enumFromThenTo = x1 =>
x2 => y => {
const d = x2 - x1;
return Array.from({
length: Math.floor(y - x2) / d + 2
}, (_, i) => x1 + (d * i));
};
// filterGen :: (a -> Bool) -> Gen [a] -> [a]
const filterGen = p => xs => {
function* go() {
let x = xs.next();
while (!x.done) {
let v = x.value;
if (p(v)) {
yield v;
}
x = xs.next();
}
}
return go(xs);
};
// head :: [a] -> a
const head = xs => (
ys => ys.length ? (
ys[0]
) : undefined
)(list(xs));
// last :: [a] -> a
const last = xs => (
// The last item of a list.
ys => 0 < ys.length ? (
ys.slice(-1)[0]
) : undefined
)(list(xs));
// list :: StringOrArrayLike b => b -> [a]
const list = xs =>
// xs itself, if it is an Array,
// or an Array derived from xs.
Array.isArray(xs) ? (
xs
) : Array.from(xs || []);
// showList :: [a] -> String
const showList = xs =>
unlines(
chunksOf(5)(xs).map(
ys => '\t' + ys.join(',')
)
) + '\n';
// succ :: Enum a => a -> a
const succ = x => {
const t = typeof x;
return 'number' !== t ? (() => {
const [i, mx] = [x, maxBound(x)].map(fromEnum);
return i < mx ? (
toEnum(x)(1 + i)
) : Error('succ :: enum out of range.')
})() : x < Number.MAX_SAFE_INTEGER ? (
1 + x
) : Error('succ :: Num out of range.')
};
// take :: Int -> [a] -> [a]
// take :: Int -> String -> String
const take = n =>
// The first n elements of a list,
// string of characters, or stream.
xs => 'GeneratorFunction' !== xs
.constructor.constructor.name ? (
xs.slice(0, n)
) : [].concat.apply([], Array.from({
length: n
}, () => {
const x = xs.next();
return x.done ? [] : [x.value];
}));
// toEnum :: a -> Int -> a
const toEnum = e =>
// The first argument is a sample of the type
// allowing the function to make the right mapping
x => ({
'number': Number,
'string': String.fromCodePoint,
'boolean': Boolean,
'object': v => e.min + v
} [typeof e])(x);
// unlines :: [String] -> String
const unlines = xs =>
// A single string formed by the intercalation
// of a list of strings with the newline character.
xs.join('\n');
// words :: String -> [String]
const words = s =>
// List of space-delimited sub-strings.
s.split(/\s+/);
// MAIN ---
return main();
})();