RosettaCodeData/Task/Fractran/JavaScript/fractran-2.js

243 lines
6.0 KiB
JavaScript

(() => {
'use strict';
// fractran :: [Ratio Int] -> Int -> Gen [Int]
const fractran = (xs, n) => {
function* go(n) {
const p = r => 0 === v % r.d;
let
v = n,
mb = find(p, xs);
yield v
while (!mb.Nothing) {
mb = bindMay(
find(p, xs),
r => (
v = truncate({
type: 'Ratio',
n: v * r.n,
d: r.d
}),
Just(v)
)
);
mb.Just && (yield v)
}
};
return go(n);
};
// readRatios :: String -> [Ratio]
const readRatios = s =>
map(x => ratio(...map(read, splitOn('/', x))),
splitOn(',', s)
);
// main :: IO()
const main = () => {
// strRatios :: String
const strRatios = `17/91, 78/85, 19/51, 23/38, 29/33, 77/29,
95/23 , 77/19, 1/17, 11/13, 13/11, 15/14, 15/2, 55/1`;
showLog(
'First fifteen steps:',
take(15,
fractran(readRatios(strRatios), 2)
)
);
showLog(
'First five primes:',
take(5,
mapMaybeGen(
x => fmapMay(
succ,
elemIndex(
2,
takeWhileGen(
even,
iterate(n => div(n, 2), x)
)
)
),
fractran(readRatios(strRatios), 2)
)
)
);
};
// GENERIC ABSTRACTIONS ----------------------------
// Just :: a -> Maybe a
const Just = x => ({
type: 'Maybe',
Nothing: false,
Just: x
});
// Nothing :: Maybe a
const Nothing = () => ({
type: 'Maybe',
Nothing: true,
});
// Tuple (,) :: a -> b -> (a, b)
const Tuple = (a, b) => ({
type: 'Tuple',
'0': a,
'1': b,
length: 2
});
// abs :: Num -> Num
const abs = Math.abs;
// bindMay (>>=) :: Maybe a -> (a -> Maybe b) -> Maybe b
const bindMay = (mb, mf) =>
mb.Nothing ? mb : mf(mb.Just);
// div :: Int -> Int -> Int
const div = (x, y) => Math.floor(x / y);
// elemIndex :: Eq a => a -> [a] -> Maybe Int
const elemIndex = (x, xs) => {
const i = xs.indexOf(x);
return -1 === i ? (
Nothing()
) : Just(i);
};
// even :: Int -> Bool
const even = n => 0 === n % 2;
// find :: (a -> Bool) -> [a] -> Maybe a
const find = (p, xs) => {
for (let i = 0, lng = xs.length; i < lng; i++) {
if (p(xs[i])) return Just(xs[i]);
}
return Nothing();
};
// fmapMay (<$>) :: (a -> b) -> Maybe a -> Maybe b
const fmapMay = (f, mb) =>
mb.Nothing ? (
mb
) : Just(f(mb.Just));
// foldl :: (a -> b -> a) -> a -> [b] -> a
const foldl = (f, a, xs) => xs.reduce(f, a);
// gcd :: Int -> Int -> Int
const gcd = (x, y) => {
const
_gcd = (a, b) => (0 === b ? a : _gcd(b, a % b)),
abs = Math.abs;
return _gcd(abs(x), abs(y));
};
// iterate :: (a -> a) -> a -> Gen [a]
function* iterate(f, x) {
let v = x;
while (true) {
yield(v);
v = f(v);
}
}
// map :: (a -> b) -> [a] -> [b]
const map = (f, xs) => xs.map(f);
// mapMaybeGen :: (a -> Maybe b) -> Gen [a] -> [b]
function* mapMaybeGen(mf, gen) {
let v = take(1, gen);
while (0 < v.length) {
let mb = mf(v[0]);
if (!mb.Nothing) yield mb.Just
v = take(1, gen);
}
}
// properFracRatio :: Ratio -> (Int, Ratio)
const properFracRatio = nd => {
const [q, r] = Array.from(quotRem(nd.n, nd.d));
return Tuple(q, ratio(r, nd.d));
};
// quot :: Int -> Int -> Int
const quot = (n, m) => Math.floor(n / m);
// quotRem :: Int -> Int -> (Int, Int)
const quotRem = (m, n) =>
Tuple(Math.floor(m / n), m % n);
// ratio :: Int -> Int -> Ratio Int
const ratio = (n, d) =>
0 !== d ? (() => {
const g = gcd(n, d);
return {
type: 'Ratio',
'n': quot(n, g), // numerator
'd': quot(d, g) // denominator
}
})() : undefined;
// read :: Read a => String -> a
const read = JSON.parse;
// showLog :: a -> IO ()
const showLog = (...args) =>
console.log(
args
.map(JSON.stringify)
.join(' -> ')
);
// snd :: (a, b) -> b
const snd = tpl => tpl[1];
// splitOn :: [a] -> [a] -> [[a]]
// splitOn :: String -> String -> [String]
const splitOn = (pat, src) =>
src.split(pat);
// succ :: Int -> Int
const succ = x =>
1 + x;
// take :: Int -> [a] -> [a]
// take :: Int -> String -> String
const take = (n, xs) =>
xs.constructor.constructor.name !== 'GeneratorFunction' ? (
xs.slice(0, n)
) : [].concat.apply([], Array.from({
length: n
}, () => {
const x = xs.next();
return x.done ? [] : [x.value];
}));
// takeWhileGen :: (a -> Bool) -> Gen [a] -> [a]
const takeWhileGen = (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;
};
// truncate :: Num -> Int
const truncate = x =>
'Ratio' === x.type ? (
properFracRatio(x)[0]
) : properFraction(x)[0];
// MAIN ---
return main();
})();