RosettaCodeData/Task/Attractive-numbers/JavaScript/attractive-numbers.js

221 lines
5.3 KiB
JavaScript

(() => {
'use strict';
// attractiveNumbers :: () -> Gen [Int]
const attractiveNumbers = () =>
// An infinite series of attractive numbers.
filter(
compose(isPrime, length, primeFactors)
)(enumFrom(1));
// ----------------------- TEST -----------------------
// main :: IO ()
const main = () =>
showCols(10)(
takeWhile(ge(120))(
attractiveNumbers()
)
);
// ---------------------- PRIMES ----------------------
// isPrime :: Int -> Bool
const isPrime = n => {
// True if n is prime.
if (2 === n || 3 === n) {
return true
}
if (2 > n || 0 === n % 2) {
return false
}
if (9 > n) {
return true
}
if (0 === n % 3) {
return false
}
return !enumFromThenTo(5)(11)(
1 + Math.floor(Math.pow(n, 0.5))
).some(x => 0 === n % x || 0 === n % (2 + x));
};
// primeFactors :: Int -> [Int]
const primeFactors = n => {
// A list of the prime factors of n.
const
go = x => {
const
root = Math.floor(Math.sqrt(x)),
m = until(
([q, _]) => (root < q) || (0 === (x % q))
)(
([_, r]) => [step(r), 1 + r]
)([
0 === x % 2 ? (
2
) : 3,
1
])[0];
return m > root ? (
[x]
) : ([m].concat(go(Math.floor(x / m))));
},
step = x => 1 + (x << 2) - ((x >> 1) << 1);
return go(n);
};
// ---------------- GENERIC FUNCTIONS -----------------
// 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) =>
fs.reduce(
(f, g) => x => f(g(x)),
x => x
);
// 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 = 1 + 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));
};
// filter :: (a -> Bool) -> Gen [a] -> [a]
const filter = 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);
};
// ge :: Ord a => a -> a -> Bool
const ge = x =>
// True if x >= y
y => x >= y;
// justifyRight :: Int -> Char -> String -> String
const justifyRight = n =>
// The string s, preceded by enough padding (with
// the character c) to reach the string length n.
c => s => n > s.length ? (
s.padStart(n, c)
) : s;
// last :: [a] -> a
const last = xs =>
// The last item of a list.
0 < xs.length ? xs.slice(-1)[0] : undefined;
// length :: [a] -> Int
const length = xs =>
// Returns Infinity over objects without finite
// length. This enables zip and zipWith to choose
// the shorter argument when one is non-finite,
// like cycle, repeat etc
(Array.isArray(xs) || 'string' === typeof xs) ? (
xs.length
) : Infinity;
// map :: (a -> b) -> [a] -> [b]
const map = f =>
// The list obtained by applying f
// to each element of xs.
// (The image of xs under f).
xs => (
Array.isArray(xs) ? (
xs
) : xs.split('')
).map(f);
// showCols :: Int -> [a] -> String
const showCols = w => xs => {
const
ys = xs.map(str),
mx = last(ys).length;
return unlines(chunksOf(w)(ys).map(
row => row.map(justifyRight(mx)(' ')).join(' ')
))
};
// str :: a -> String
const str = x =>
x.toString();
// takeWhile :: (a -> Bool) -> Gen [a] -> [a]
const takeWhile = p => xs => {
const ys = [];
let
nxt = xs.next(),
v = nxt.value;
while (!nxt.done && p(v)) {
ys.push(v);
nxt = xs.next();
v = nxt.value
}
return ys;
};
// 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');
// until :: (a -> Bool) -> (a -> a) -> a -> a
const until = p => f => x => {
let v = x;
while (!p(v)) v = f(v);
return v;
};
// MAIN ---
return main();
})();