109 lines
2.8 KiB
JavaScript
109 lines
2.8 KiB
JavaScript
(() => {
|
|
"use strict";
|
|
|
|
// vanEck :: Int -> [Int]
|
|
const vanEck = n =>
|
|
// First n terms of the vanEck series.
|
|
[0].concat(mapAccumL(
|
|
([x, seen]) => i => {
|
|
const
|
|
prev = seen[x],
|
|
v = Boolean(prev) ? (
|
|
i - prev
|
|
) : 0;
|
|
|
|
return [
|
|
[v, (seen[x] = i, seen)], v
|
|
];
|
|
})(
|
|
[0, {}]
|
|
)(
|
|
enumFromTo(1)(n - 1)
|
|
)[1]);
|
|
|
|
|
|
// ----------------------- TEST ------------------------
|
|
const main = () =>
|
|
fTable(
|
|
"Terms of the VanEck series:\n"
|
|
)(
|
|
n => `${str(n - 10)}-${str(n)}`
|
|
)(
|
|
xs => JSON.stringify(xs.slice(-10))
|
|
)(
|
|
vanEck
|
|
)([10, 1000, 10000]);
|
|
|
|
|
|
// ----------------- GENERIC FUNCTIONS -----------------
|
|
|
|
// enumFromTo :: Int -> Int -> [Int]
|
|
const enumFromTo = m =>
|
|
n => Array.from({
|
|
length: 1 + n - m
|
|
}, (_, i) => m + i);
|
|
|
|
|
|
// fTable :: String -> (a -> String) ->
|
|
// (b -> String) -> (a -> b) -> [a] -> String
|
|
const fTable = s =>
|
|
// Heading -> x display function ->
|
|
// fx display function ->
|
|
// f -> values -> tabular string
|
|
xShow => fxShow => f => xs => {
|
|
const
|
|
ys = xs.map(xShow),
|
|
w = Math.max(...ys.map(y => [...y].length)),
|
|
table = zipWith(
|
|
a => b => `${a.padStart(w, " ")} -> ${b}`
|
|
)(ys)(
|
|
xs.map(x => fxShow(f(x)))
|
|
).join("\n");
|
|
|
|
return `${s}\n${table}`;
|
|
};
|
|
|
|
|
|
// mapAccumL :: (acc -> x -> (acc, y)) -> acc ->
|
|
// [x] -> (acc, [y])
|
|
const mapAccumL = f =>
|
|
// A tuple of an accumulation and a list
|
|
// obtained by a combined map and fold,
|
|
// with accumulation from left to right.
|
|
acc => xs => [...xs].reduce(
|
|
([a, bs], x) => second(
|
|
v => bs.concat(v)
|
|
)(
|
|
f(a)(x)
|
|
),
|
|
[acc, []]
|
|
);
|
|
|
|
|
|
// second :: (a -> b) -> ((c, a) -> (c, b))
|
|
const second = f =>
|
|
// A function over a simple value lifted
|
|
// to a function over a tuple.
|
|
// f (a, b) -> (a, f(b))
|
|
([x, y]) => [x, f(y)];
|
|
|
|
|
|
// str :: a -> String
|
|
const str = x => x.toString();
|
|
|
|
|
|
// zipWith :: (a -> b -> c) -> [a] -> [b] -> [c]
|
|
const zipWith = f =>
|
|
// A list constructed by zipping with a
|
|
// custom function, rather than with the
|
|
// default tuple constructor.
|
|
xs => ys => xs.map(
|
|
(x, i) => f(x)(ys[i])
|
|
).slice(
|
|
0, Math.min(xs.length, ys.length)
|
|
);
|
|
|
|
// MAIN ---
|
|
return main();
|
|
})();
|