RosettaCodeData/Task/Probabilistic-choice/JavaScript/probabilistic-choice-2.js

107 lines
3.0 KiB
JavaScript

(() => {
'use strict';
// GENERIC FUNCTIONS -----------------------------------------------------
// transpose :: [[a]] -> [[a]]
const transpose = xs =>
xs[0].map((_, iCol) => xs.map(row => row[iCol]));
// justifyLeft :: Int -> Char -> Text -> Text
const justifyLeft = (n, cFiller, strText) =>
n > strText.length ? (
(strText + cFiller.repeat(n))
.substr(0, n)
) : strText;
// 2 or more arguments
// curry :: Function -> Function
const curry = (f, ...args) => {
const go = xs => xs.length >= f.length ? (f.apply(null, xs)) :
function () {
return go(xs.concat([].slice.apply(arguments)));
};
return go([].slice.call(args, 1));
};
// zipWith :: (a -> b -> c) -> [a] -> [b] -> [c]
const zipWith = (f, xs, ys) => {
const ny = ys.length;
return (xs.length <= ny ? xs : xs.slice(0, ny))
.map((x, i) => f(x, ys[i]));
};
// subtract :: (Num a) => a -> a -> a
const subtract = (x, y) => y - x;
// scanl1 :: (a -> a -> a) -> [a] -> [a]
const scanl1 = (f, xs) =>
xs.length > 0 ? scanl(f, xs[0], xs.slice(1)) : [];
// scanl :: (b -> a -> b) -> b -> [a] -> [b]
const scanl = (f, startValue, xs) =>
xs.reduce((a, x) => {
const v = f(a.acc, x);
return {
acc: v,
scan: a.scan.concat(v)
};
}, {
acc: startValue,
scan: [startValue]
})
.scan;
// unwords :: [String] -> String
const unwords = xs => xs.join(' ');
// PROBABILISTIC CHOICE --------------------------------------------------
// samples :: Int -> Int -> [Float]
const samples = n =>
Array.from({
length: n
}, Math.random);
// thresholds :: Float
const thresholds = scanl1(
(a, b) => a + b, [5, 6, 7, 8, 9, 10, 11].map(x => 1 / x)
)
.concat(1);
// expected :: Float -> Float
const expected = limits =>
limits.map((x, i, xs) => i > 0 ? (x - xs[i - 1]) : x);
// dataBinCounts :: [Float] -> [Float] -> [Int]
const dataBinCounts = (thresholds, samples) => {
const
lng = samples.length,
xs = thresholds
.map(x => lng - samples.filter(v => v > x)
.length);
return zipWith(subtract, [0].concat(xs), xs.concat(lng));
};
// intSamples :: Integer
const intSamples = 1000000;
// aligned :: a -> String
const aligned = x => justifyLeft(12, ' ', isNaN(x) ? x : x.toFixed(7));
return transpose([
['', 'Aleph', 'Beit', 'Gimel', 'Dalet', 'He', 'Vav', 'Zayin', 'Chet']
.map(curry(justifyLeft)(7, ' ')),
['Expected'].concat(expected(thresholds))
.map(aligned),
['Observed'].concat(dataBinCounts(thresholds, samples(intSamples))
.map(x => x / intSamples))
.map(aligned)
])
.map(unwords)
.join('\n');
})();