Cantor set: Difference between revisions

→‎JavaScript :: Dual representation: Updated primitives, tidied.
(→‎JavaScript :: Dual representation: Updated primitives, tidied.)
Line 1,792:
 
<lang javascript>(() => {
'"use strict'";
 
// --------------------- CANTOR ----------------------
// main :: IO ()
const main = () => {
 
// cantor :: [(Rational, Rational)] ->
const xs = take(4, iterate(cantor, [Tuple(0, 1)]));
// [(Rational, Rational)]
 
console.log(
unlines(map(intervalRatios, xs)) + '\n',
);
console.log(
intervalBars(xs)
);
};
 
// cantor :: [(Rational, Rational)] -> [(Rational, Rational)]
const cantor = xs => {
const go = tplab => {
const [r1, r2] = map(rational, Array.from(tplab).map(rational);
const third = ratioDiv(ratioMinus(r2, )(r1), )(3);
 
return [
Tuple(r1, )(ratioPlus(r1, )(third)),
Tuple(ratioMinus(r2, )(third), )(r2)
];
};
 
return concatMap(go, xs);
return xs.flatMap(go);
};
 
 
// ---------------------- TEST -----------------------
// main :: IO ()
const main = () => {
const
xs = take(4)(
iterate(cantor)([Tuple(0)(1)])
);
 
return [
`${unlines(xs.map(intervalRatios))}\n`,
intervalBars(xs)
]
.join("\n\n");
};
 
 
// --------------------- DISPLAY ---------------------
 
// intervalRatios :: [(Rational, Rational)] -> String
const intervalRatios = xs => {
const go = tpl =>
mapArray.from(compose(showRatio, rationaltpl),.map(
Array.fromcompose(tplshowRatio, rational)
).join(', ');
return '(' + map(go, xs).join(') (') +", '")';
 
return `(${xs.map(go).join(") (")})`;
};
 
// intervalBars :: [[(Rational, Rational)]] -> String
const intervalBars = xsrs => {
const go = w => xs =>
concat(snd(mapAccumL(
(a, => tpl) => {
const [wx, wy] = Array.from(tpl).map(
r => ratioMult(w, rational)(r)),
Array.from rational(tplr)
)
);
 
return Tuple(
return Tuple(wy,)(
replicateString(floor(ratioMinus(wx, a)), ' ') +
replicateString( floor(ratioMinus(wy, wx)(a), '█')
)(" ") + replicateString(
floor(ratioMinus(wy)(wx))
)("█")
);
},
)(0, )(xs)).join("");
const d = )));maximum(
const d = maximum last(rs).map(x => fst(x).d, last(xs)));
return unlines(map(go(ratio(d, 1)), xs));
 
return unlines(rs.map(
go(Ratio(d)(1))
));
};
 
 
// GENERIC FUNCTIONS ------------------ GENERIC FUNCTIONS ----------------
 
// Ratio :: Integral a => a -> a -> Ratio a
const Ratio = a => b => {
const go = (x, y) =>
0 !== y ? (() => {
const d = gcd(x)(y);
 
return {
type: "Ratio",
// numerator
"n": Math.trunc(x / d),
// denominator
"d": Math.trunc(y / d)
};
})() : undefined;
 
return go(a * signum(b), abs(b));
};
 
 
// Tuple (,) :: a -> b -> (a, b)
const Tuple = (a, b) => ({
type:b 'Tuple',=> ({
'0' type: a"Tuple",
'1' "0": ba,
length "1": 2b,
length: 2
});
});
 
 
// abs :: Num -> Num
const abs = Math.abs;
// Absolute value of a given number
// without the sign.
x => 0 > x ? (
-x
) : x;
 
// compose (<<<) :: (b -> c) -> (a -> b) -> a -> c
const compose = (f, g) => x => f(g(x));
 
// concatapproxRatio :: [[a]]Float -> [a]Float -> Ratio
//const concatapproxRatio ::= [String]eps -=> String
const concat = xs n => {
0 < xs.length ? (() => {const
const unit = 'string' !=gcde = typeof(e, xs[0]x, ?y) (=> {
[] const _gcd = (a, b) =>
) : ''; b < e ? (
return unit.concat.apply(unit, xs); a
})( ) : []_gcd(b, a % b);
 
return _gcd(Math.abs(x), Math.abs(y));
},
c = gcde(Boolean(eps) ? (
eps
) : (1 / 10000), 1, n);
 
return Ratio(
Math.floor(n / c)
)(
Math.floor(1 / c)
);
};
 
// concatMap :: (a -> [b]) -> [a] -> [b]
const concatMap = (f, xs) =>
xs.reduce((a, x) => a.concat(f(x)), []);
 
// floor :: Num -> Int
Line 1,885 ⟶ 1,940:
const
nr = (
'"Ratio'" !== x.type ? (
properFraction
) : properFracRatio
)(x),
n = nr[0];
 
return 0 > nr[1] ? n - 1 : n;
};
 
// foldl1 :: (a -> a -> a) -> [a] -> a
const foldl1 = (f, xs) =>
1 < xs.length ? xs.slice(1)
.reduce(f, xs[0]) : xs[0];
 
// fst :: (a, b) -> a
const fst = tpl => tpl[0];
// First member of a pair.
tpl[0];
 
 
// gcd :: Integral a => a -> a -> a
const gcd = x =>
y => {
const zero = x.constructor(0);
const go = (a, b) =>
zero === b ? (
a
) : go(b, a % b);
 
return go(abs(x), abs(y));
};
 
 
// 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
);
 
// 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*const iterate(f, x)= f {=>
let// vAn =infinite x;list of repeated
while// (true)applications {of f to x.
function* yield(vx); {
let v = f(v)x;
 
}
while (true) {
}
yield v;
v = f(v);
}
};
 
 
// last :: [a] -> a
const last = xs =>
0// <The xs.lengthlast ?item xs.slice(-1)[0]of :a undefined;list.
0 < xs.length ? (
xs.slice(-1)[0]
) : null;
 
 
// lcm :: Int -> Int -> Int
const lcm = (x, y) =>
// The smallest positive integer divisible
(x === 0 || y === 0) ? 0 : Math.abs(Math.floor(x / gcd(x, y)) * y);
// without remainder by both x and y.
y => (x === 0 || y === 0) ? (
0
) : Math.abs(Math.floor(x / gcd(x)(y)) * y);
 
// map :: (a -> b) -> [a] -> [b]
const map = (f, xs) => xs.map(f);
 
// 'The mapAccumL function behaves:: like(acc a-> combinationx of-> map(acc, andy)) foldl;->
// acc -> [x] -> (acc, [y])
// it applies a function to each element of a list, passing an accumulating
const mapAccumL = f =>
// parameter from left to right, and returning a final value of this
// A tuple of an accumulation and a list
// accumulator together with the new list.' (See Hoogle)
// obtained by a combined map and fold,
// with accumulation from left to right.
acc => xs => [...xs].reduce(
(a, x) => {
const tpl = f(a[0])(x);
 
return [tpl[0], a[1].concat(tpl[1])];
},
[acc, []]
);
 
// mapAccumL :: (acc -> x -> (acc, y)) -> acc -> [x] -> (acc, [y])
const mapAccumL = (f, acc, xs) =>
xs.reduce((a, x, i) => {
const pair = f(a[0], x, i);
return Tuple(pair[0], a[1].concat(pair[1]));
}, Tuple(acc, []));
 
// maximum :: Ord a => [a] -> a
const maximum = xs => (
0// <The xs.lengthlargest ?value (in a non-empty list.
foldl1((a, x)ys => x0 >< ays.length ? x : a, xs)(
) : undefined; ys.slice(1).reduce(
(a, y) => y > a ? (
y
) : a, ys[0]
)
) : undefined
)(xs);
 
 
// properFracRatio :: Ratio -> (Int, Ratio)
const properFracRatio = nd => {
const [q, r] = Array.from(quotRem(nd.n, )(nd.d));
 
return Tuple(q, ratio(r, nd.d));
return Tuple(q)(Ratio(r)(nd.d));
};
 
 
// properFraction :: Real -> (Int, Real)
const properFraction = n => {
const i = Math.floor(n) + (n < 0 ? 1 : 0);
 
return Tuple(i, n - i);
return Tuple(i)(n - i);
};
 
// quot :: Int -> Int -> Int
const quot = (n, m) => Math.floor(n / m);
 
// quotRem :: IntIntegral a => a -> Inta -> (Inta, Inta)
const quotRem = (m, n) =>
// The quotient, tupled with the remainder.
Tuple(Math.floor(m / n), m % n);
n => Tuple(
Math.trunc(m / n)
)(
m % n
);
 
// ratio :: Int -> Int -> Ratio Int
const ratio = (x, y) => {
const go = (x, y) =>
0 !== y ? (() => {
const d = gcd(x, y);
return {
type: 'Ratio',
'n': quot(x, d), // numerator
'd': quot(y, d) // denominator
};
})() : undefined;
return go(x * signum(y), abs(y));
};
 
// ratioDiv :: Rational -> Rational -> Rational
const ratioDiv = (n1, => n2) => {
const [r1, r2] = map(rational, [n1, n2].map(rational);
 
return ratio(r1.n * r2.d, r1.d * r2.n);
return Ratio(r1.n * r2.d)(
r1.d * r2.n
);
};
 
 
// ratioMinus :: Rational -> Rational -> Rational
const ratioMinus = (n1, => n2) => {
const [r1, r2] = map(rational, [n1, n2].map(rational);
const d = lcm(r1.d, )(r2.d);
 
return ratio(
return Ratio(
(r1.n * (d / r1.d)) - (r2.n * (d / r2.d)),
(r1.n * (d / r1.d)) - (r2.n * (d / r2.d))
)(d);
};
 
 
// ratioMult :: Rational -> Rational -> Rational
const ratioMult = (n1, => n2) => {
const [r1, r2] = map(rational, [n1, n2].map(rational);
return ratio(r1.n * r2.n, r1.d * r2.d);
};
 
return Ratio(r1.n * r2.n)(
// ratioPlus :: Rational -> Rational -> Rational
const ratioPlus = (n1, n2) => { r1.d * r2.d
const [r1, r2] = map(rational, [n1, n2]);
const d = lcm(r1.d, r2.d);
return ratio(
(r1.n * (d / r1.d)) + (r2.n * (d / r2.d)),
d
);
};
 
 
// ratioPlus :: Rational -> Rational -> Rational
const ratioPlus = n1 =>
n2 => {
const [r1, r2] = [n1, n2].map(rational);
const d = lcm(r1.d)(r2.d);
 
return Ratio(
(r1.n * (d / r1.d)) + (
r2.n * (d / r2.d)
)
)(d);
};
 
 
// rational :: Num a => a -> Rational
const rational = x =>
isNaN(x) ? x : ratioNumber.isInteger(x, 1); ? (
Ratio(x)(1)
) : approxRatio(undefined)(x);
 
 
// replicateString :: Int -> String -> String
const replicateString = (n, s) => s.repeat(n);
s => s.repeat(n);
 
 
// showRatio :: Ratio -> String
const showRatio = r =>
'"Ratio'" !== r.type ? (
r.toString()
) : r.n.toString() + (
1 !== r.d ? (
'`/' + ${r.d.toString()}`
) : ''""
);
 
 
// signum :: Num -> Num
const signum = n => 0 > n ? -1 : (0 < n ? 1 : 0);
// | Sign of a number.
n.constructor(
0 > n ? (
-1
) : (
0 < n ? 1 : 0
)
);
 
 
// snd :: (a, b) -> b
const snd = tpl => tpl[1];
// Second member of a pair.
tpl[1];
 
 
// take :: Int -> [a] -> [a]
// take :: Int -> String -> String
const take = (n, xs) =>
// The first n elements of a list,
'GeneratorFunction' !== xs.constructor.constructor.name ? (
// 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];
}));
 
 
// unlines :: [String] -> String
const unlines = xs => xs.join('\n');
// A single string formed by the intercalation
// of a list of strings with the newline character.
xs.join("\n");
 
 
// MAIN ---
9,655

edits