73 lines
2.3 KiB
JavaScript
73 lines
2.3 KiB
JavaScript
(() => {
|
|
|
|
// 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))));
|
|
})();
|