209 lines
5.1 KiB
JavaScript
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();
|
|
})();
|