September 2017 Update
This commit is contained in:
parent
bba7bfd280
commit
ba8067c3b7
14570 changed files with 153136 additions and 63871 deletions
|
|
@ -0,0 +1,73 @@
|
|||
(() => {
|
||||
|
||||
// magicSquare :: Int -> [[Int]]
|
||||
const magicSquare = n =>
|
||||
n % 2 !== 0 ? (
|
||||
compose([transpose, cycled, transpose, cycled, enumSquare])(n)
|
||||
) : [];
|
||||
|
||||
// Size of square -> rows containing integers [1..]
|
||||
// enumSquare :: Int -> [[Int]]
|
||||
const enumSquare = n =>
|
||||
chunksOf(n, enumFromTo(1, n * n));
|
||||
|
||||
// Table of integers -> Table with rows rotated by descending deltas
|
||||
// cycled :: [[Int]] -> [[Int]]
|
||||
const cycled = rows => {
|
||||
const d = Math.floor(rows.length / 2);
|
||||
return zipWith(listCycle, enumFromTo(d, -d), rows)
|
||||
};
|
||||
|
||||
// Number of positions to shift to right -> List -> Wrap-cycled list
|
||||
// listCycle :: Int -> [a] -> [a]
|
||||
const listCycle = (n, xs) => {
|
||||
const d = -(n % xs.length);
|
||||
return (d !== 0 ? xs.slice(d)
|
||||
.concat(xs.slice(0, d)) : xs);
|
||||
};
|
||||
|
||||
// GENERIC FUNCTIONS ------------------------------------------------------
|
||||
|
||||
// chunksOf :: Int -> [a] -> [[a]]
|
||||
const chunksOf = (n, xs) =>
|
||||
xs.reduce((a, _, i, xs) =>
|
||||
i % n ? a : a.concat([xs.slice(i, i + n)]), []);
|
||||
|
||||
// compose :: [(a -> a)] -> (a -> a)
|
||||
const compose = fs => x => fs.reduceRight((a, f) => f(a), x);
|
||||
|
||||
// enumFromTo :: Int -> Int -> Maybe Int -> [Int]
|
||||
const enumFromTo = (m, n, step) => {
|
||||
const d = (step || 1) * (n >= m ? 1 : -1);
|
||||
return Array.from({
|
||||
length: Math.floor((n - m) / d) + 1
|
||||
}, (_, i) => m + (i * d));
|
||||
};
|
||||
|
||||
// intercalate :: String -> [a] -> String
|
||||
const intercalate = (s, xs) => xs.join(s);
|
||||
|
||||
// min :: Ord a => a -> a -> a
|
||||
const min = (a, b) => b < a ? b : a;
|
||||
|
||||
// show :: a -> String
|
||||
const show = JSON.stringify;
|
||||
|
||||
// transpose :: [[a]] -> [[a]]
|
||||
const transpose = xs =>
|
||||
xs[0].map((_, iCol) => xs.map(row => row[iCol]));
|
||||
|
||||
// unlines :: [String] -> String
|
||||
const unlines = xs => xs.join('\n');
|
||||
|
||||
// zipWith :: (a -> b -> c) -> [a] -> [b] -> [c]
|
||||
const zipWith = (f, xs, ys) =>
|
||||
Array.from({
|
||||
length: min(xs.length, ys.length)
|
||||
}, (_, i) => f(xs[i], ys[i]));
|
||||
|
||||
// TEST -------------------------------------------------------------------
|
||||
return intercalate('\n\n', [3, 5, 7]
|
||||
.map(magicSquare)
|
||||
.map(xs => unlines(xs.map(show))));
|
||||
})();
|
||||
|
|
@ -0,0 +1,167 @@
|
|||
(() => {
|
||||
|
||||
// Number of rows -> n rows of integers
|
||||
// oddMagicTable :: Int -> [[Int]]
|
||||
const oddMagicTable = n =>
|
||||
mapAsTable(n, siamMap(quot(n, 2)));
|
||||
|
||||
// Highest index of square -> Siam xys so far -> xy -> next xy coordinate
|
||||
// nextSiam :: Int -> M.Map (Int, Int) Int -> (Int, Int) -> (Int, Int)
|
||||
const nextSiam = (uBound, sMap, [x, y]) => {
|
||||
const [a, b] = [x + 1, y - 1];
|
||||
return (a > uBound && b < 0) ? (
|
||||
[uBound, 1] // Move down if obstructed by corner
|
||||
) : a > uBound ? (
|
||||
[0, b] // Wrap at right edge
|
||||
) : b < 0 ? (
|
||||
[a, uBound] // Wrap at upper edge
|
||||
) : mapLookup(sMap, [a, b])
|
||||
.nothing ? ( // Unimpeded default: one up one right
|
||||
[a, b]
|
||||
) : [a - 1, b + 2]; // Position occupied: move down
|
||||
};
|
||||
|
||||
// Order of table -> Siamese indices keyed by coordinates
|
||||
// siamMap :: Int -> M.Map (Int, Int) Int
|
||||
const siamMap = n => {
|
||||
const
|
||||
uBound = 2 * n,
|
||||
sPath = (uBound, sMap, xy, n) => {
|
||||
const [x, y] = xy,
|
||||
newMap = mapInsert(sMap, xy, n);
|
||||
return (y == uBound && x == quot(uBound, 2) ? (
|
||||
newMap
|
||||
) : sPath(
|
||||
uBound, newMap, nextSiam(uBound, newMap, [x, y]), n + 1));
|
||||
};
|
||||
return sPath(uBound, {}, [n, 0], 1);
|
||||
};
|
||||
|
||||
// Size of square -> integers keyed by coordinates -> rows of integers
|
||||
// mapAsTable :: Int -> M.Map (Int, Int) Int -> [[Int]]
|
||||
const mapAsTable = (nCols, dct) => {
|
||||
const axis = enumFromTo(0, nCols - 1);
|
||||
return map(row => map(k => fromJust(mapLookup(dct, k)), row),
|
||||
bind(axis, y => [bind(axis, x => [
|
||||
[x, y]
|
||||
])]));
|
||||
};
|
||||
|
||||
// GENERIC FUNCTIONS ------------------------------------------------------
|
||||
|
||||
// bind :: [a] -> (a -> [b]) -> [b]
|
||||
const bind = (xs, f) => [].concat.apply([], xs.map(f));
|
||||
|
||||
// curry :: Function -> Function
|
||||
const curry = (f, ...args) => {
|
||||
const go = xs => xs.length >= f.length ? (f.apply(null, xs)) :
|
||||
function () {
|
||||
return go(xs.concat(Array.from(arguments)));
|
||||
};
|
||||
return go([].slice.call(args, 1));
|
||||
};
|
||||
|
||||
// enumFromTo :: Int -> Int -> [Int]
|
||||
const enumFromTo = (m, n) =>
|
||||
Array.from({
|
||||
length: Math.floor(n - m) + 1
|
||||
}, (_, i) => m + i);
|
||||
|
||||
// fromJust :: M a -> a
|
||||
const fromJust = m => m.nothing ? {} : m.just;
|
||||
|
||||
// fst :: [a, b] -> a
|
||||
const fst = pair => pair.length === 2 ? pair[0] : undefined;
|
||||
|
||||
// intercalate :: String -> [a] -> String
|
||||
const intercalate = (s, xs) => xs.join(s);
|
||||
|
||||
// justifyRight :: Int -> Char -> Text -> Text
|
||||
const justifyRight = (n, cFiller, strText) =>
|
||||
n > strText.length ? (
|
||||
(cFiller.repeat(n) + strText)
|
||||
.slice(-n)
|
||||
) : strText;
|
||||
|
||||
// length :: [a] -> Int
|
||||
const length = xs => xs.length;
|
||||
|
||||
// log :: a -> IO ()
|
||||
const log = (...args) =>
|
||||
console.log(
|
||||
args
|
||||
.map(show)
|
||||
.join(' -> ')
|
||||
);
|
||||
|
||||
// map :: (a -> b) -> [a] -> [b]
|
||||
const map = (f, xs) => xs.map(f);
|
||||
|
||||
// mapInsert :: Dictionary -> k -> v -> Dictionary
|
||||
const mapInsert = (dct, k, v) =>
|
||||
(dct[(typeof k === 'string' && k) || show(k)] = v, dct);
|
||||
|
||||
// mapKeys :: Map k a -> [k]
|
||||
const mapKeys = dct =>
|
||||
sortBy(mappendComparing([snd, fst]),
|
||||
map(JSON.parse, Object.keys(dct)));
|
||||
|
||||
// mapLookup :: Dictionary -> k -> Maybe v
|
||||
const mapLookup = (dct, k) => {
|
||||
const
|
||||
v = dct[(typeof k === 'string' && k) || show(k)],
|
||||
blnJust = (typeof v !== 'undefined');
|
||||
return {
|
||||
nothing: !blnJust,
|
||||
just: v
|
||||
};
|
||||
};
|
||||
|
||||
// mappendComparing :: [(a -> b)] -> (a -> a -> Ordering)
|
||||
const mappendComparing = fs => (x, y) =>
|
||||
fs.reduce((ord, f) => {
|
||||
if (ord !== 0) return ord;
|
||||
const
|
||||
a = f(x),
|
||||
b = f(y);
|
||||
return a < b ? -1 : a > b ? 1 : 0
|
||||
}, 0);
|
||||
|
||||
// maximum :: [a] -> a
|
||||
const maximum = xs =>
|
||||
xs.reduce((a, x) => (x > a || a === undefined ? x : a), undefined);
|
||||
|
||||
// Integral a => a -> a -> a
|
||||
const quot = (n, m) => Math.floor(n / m);
|
||||
|
||||
// show :: a -> String
|
||||
const show = x => JSON.stringify(x);
|
||||
//
|
||||
// snd :: (a, b) -> b
|
||||
const snd = tpl => Array.isArray(tpl) ? tpl[1] : undefined;
|
||||
//
|
||||
// sortBy :: (a -> a -> Ordering) -> [a] -> [a]
|
||||
const sortBy = (f, xs) => xs.slice()
|
||||
.sort(f);
|
||||
|
||||
// table :: String -> [[String]] -> [String]
|
||||
const table = (delim, rows) =>
|
||||
map(curry(intercalate)(delim),
|
||||
transpose(map(col =>
|
||||
map(curry(justifyRight)(maximum(map(length, col)))(' '), col),
|
||||
transpose(rows))));
|
||||
|
||||
// transpose :: [[a]] -> [[a]]
|
||||
const transpose = xs =>
|
||||
xs[0].map((_, col) => xs.map(row => row[col]));
|
||||
|
||||
// unlines :: [String] -> String
|
||||
const unlines = xs => xs.join('\n');
|
||||
|
||||
// TEST -------------------------------------------------------------------
|
||||
|
||||
return intercalate('\n\n',
|
||||
bind([3, 5, 7],
|
||||
n => unlines(table(" ",
|
||||
map(xs => map(show, xs), oddMagicTable(n))))));
|
||||
})();
|
||||
Loading…
Add table
Add a link
Reference in a new issue